Boost Fusion:将适应的结构类型转换为文本

时间:2016-08-19 07:12:18

标签: c++ introspection boost-fusion

给出这样的结构:

struct Foo
{
    int x;
    int y;
    double z;
};

BOOST_FUSION_ADAPT_STRUCT(Foo, x, y, z);       

我想生成一个这样的字符串:

"{ int x; int y; double z; }"

我已经看过如何使用Fusion改编的结构print the values,但在这里我只需打印类型和名称。

我怎么能这么简单地做到这一点?我没有和Boost.Fusion结婚,如果有更好的方式。

2 个答案:

答案 0 :(得分:5)

我认为您可以通过对this answer中的代码进行一些细微修改来获得类似于您想要的内容。您可以使用boost::fusion::extension::struct_member_name轻松获取成员名称,但据我所知,您无法直接获取成员类型名称。您可以使用boost::fusion::result_of::value_at(以及其他选项)获取成员类型,并且我已选择使用Boost.TypeIndex来获取其名称(在不同程度的可爱程度,取决于编译器和相关类型) 。所有这一切都假设你实际上需要融合适应,如果你不能,你可能会得到一个更简单的方法,只做你需要的。

完整代码
Running on WandBox (gcc)
Running on rextester (vc)

#include <iostream>
#include <string>

#include <boost/mpl/range_c.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/zip.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/mpl.hpp>

#include <boost/type_index.hpp>


namespace fusion=boost::fusion;
namespace mpl=boost::mpl;

struct Foo
{
    int x;
    int y;
    double z;
};

BOOST_FUSION_ADAPT_STRUCT(Foo, x, y, z);

struct Bar
{
    std::pair<int,int> p;
    std::string s;
};

BOOST_FUSION_ADAPT_STRUCT(Bar, p, s);

template <typename Sequence>
struct Struct_member_printer
{
    Struct_member_printer(const Sequence& seq):seq_(seq){}
    const Sequence& seq_;
    template <typename Index>
    void operator() (Index) const
    {

        std::string member_type = boost::typeindex::type_id<typename fusion::result_of::value_at<Sequence,Index>::type >().pretty_name() ;
        std::string member_name = fusion::extension::struct_member_name<Sequence,Index::value>::call();

        std::cout << member_type << " " << member_name << "; ";
    }
};
template<typename Sequence>
void print_struct(Sequence const& v)
{
    typedef mpl::range_c<unsigned, 0, fusion::result_of::size<Sequence>::value > Indices; 
    std::cout << "{ ";
    fusion::for_each(Indices(), Struct_member_printer<Sequence>(v));
    std::cout << "}\n";
}

int main()
{
    Foo foo;
    print_struct(foo);

    Bar bar;
    print_struct(bar);
}

答案 1 :(得分:0)

您可以使用以下解决方案,该解决方案依赖于编译器(在clang / gcc / MSVC上测试),并且只有在您使用c ++ 14(在稍作修改后才能使用c ++ 11)时才有效。它做你想要的,但可能有更简单的解决方案......

第一部分是一些编译器相关代码,以 demangle names 返回std::type_info::name

#include <string>

#if defined __GNUC__

#include <cxxabi.h>

std::string demangle (const char *name) {
    int status = 0;
    return abi::__cxa_demangle(name, 0, 0, &status);
}

#elif defined _WIN32 

#include <Windows.h>
#include <DbgHelp.h>

std::string demangle (const char *name) {
    char buffer[1024];
    UnDecorateSymbolName(name, buffer, sizeof(buffer)/sizeof(*buffer), 0);
    return buffer;
}

#endif

然后“通用”部分很短:

#include <array>
#include <tuple>


template <typename Tuple, size_t ...Idx>
std::string to_string (std::string vars, std::index_sequence<Idx...>) {
    std::array<const char *, std::tuple_size<Tuple>::value> tnames{
        typeid(typename std::tuple_element<Idx, Tuple>::type).name()...};
    std::stringstream res;
    res << "{ ";
    for (auto s: tnames) {
        size_t end = vars.find(',');
        res << demangle(s) << ' ' << vars.substr(0, end) << "; ";
        vars = vars.substr(end + 2);
    }
    res << '}';
    return res.str();
}

#define CREATE(S, ...) struct: S { \
    using Tuple = decltype(std::make_tuple(__VA_ARGS__)); \
    std::string operator()() { \
        return to_string<Tuple>(#__VA_ARGS__, \
            std::make_index_sequence<std::tuple_size<Tuple>::value>{}); \
    }; \
} 

我们的想法是创建一个继承自指定类(例如L)的类Foo,并使用__VA_ARGS__宏将属性名称扩展为std::make_tuple获得他们的类型。

to_stringstd::type_info::name检索每个元素的tuple,并将其与属性名称(例如"x, y, z")合并。

CREATE宏返回一个lambda,你可以使用如下:

struct Foo {
    int x;
    int y;
    double z;
};

CREATE(Foo, x, y, z) foo_xyz;

#include <iostream>

int main () {
   std::cout << foo_xyz() << std::endl; 
}

输出:

{ int x; int y; double z; }

注意:由于demangling与编译器有关,因此您可能无法获得与所有编译器完全相同的输出...例如,如果您有std::array<int, 10>

gcc: std::array<int, 10ul>
clang: std::__1::array<int, 10ul>
msvc: class std::array<int,10>

注意:支持MSVC的用法“很复杂”:最初我在CREATE内使用了lambda,这样你就可以CREATE(Foo, x, y, z)()而不必费心去创建一个变量(我不知道如何生成正确的名称 - 请参阅此答案的初始版本),但MSVC不喜欢lambda中的decltype(std::make_tuple(x, y, z)) ...(可能是一个错误)。