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

Boost Fusion: convert adapted struct type to text

给出这样的结构:

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

BOOST_FUSION_ADAPT_STRUCT(Foo, x, y, z);       

我想生成这样的字符串:

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

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

我怎样才能简单地做到这一点?如果有更好的方法,我不嫁给Boost.Fusion

您可以使用以下依赖于编译器的解决方案(在 clang / gcc / MSVC 上测试)并且仅在您拥有 c++14 时才有效(在稍作修改后应与 c++11 一起使用)。它可以满足您的需求,但可能还有更简单的解决方案...

第一部分是一些依赖于编译器的代码,用于 demangle names return by 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

然后 "generic" 部分很短:

#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>{}); \
    }; \
} 

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

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

CREATE 宏 return 是一个 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; }

注意:由于分解依赖于编译器,您可能不会在所有编译器上获得完全相同的输出...例如,如果您有一个 std::array<int, 10>

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

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

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

完整代码
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);
}