您可以使用以下解决方案,它依赖于编译器(已在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>{}); \
}; \
}
这个想法是创建一个继承自指定类(例如Foo
)的类L
,并使用__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))
...(可能是一个错误)。