给出这样的结构:
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结婚,如果有更好的方式。
答案 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_string
从std::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))
...(可能是一个错误)。