您可以使用以下依赖于编译器的解决方案(在 clang / gcc / MSVC 上测试),并且仅在您拥有 c++14 时才有效(经过轻微修改后应该可以与 c++11 一起使用)。它可以满足您的需求,但可能有更简单的解决方案...
第一部分是一些依赖于编译器的代码,用于对名称进行分解,由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 从tuple 中检索每个元素的std::type_info::name,并将其与属性名称组合(例如"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; }
注意:由于解构依赖于编译器,因此您可能无法使用所有编译器获得完全相同的输出...例如,如果您有 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)) ...(可能是一个错误)。