You can use the following solution which is compiler dependent (tested on clang / gcc / MSVC) and only works if you have c++14 (should work with c++11 after slight modifications). It does what you want but there may be simpler solutions...
First part is a bit of compiler dependent code to 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
Then the "generic" part is quite short:
#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>{}); \
}; \
}
The idea is to create a class L
that inherits from the specified class (e.g. Foo
) and use the __VA_ARGS__
macro to expands the attribute names into std::make_tuple
to obtain their types.
The to_string
retrieves the std::type_info::name
of each elements from a tuple
and combines it with the attribute names (e.g. "x, y, z"
).
The CREATE
macro returns a lambda that you can use as follow:
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;
}
Output:
{ int x; int y; double z; }
Note: Since demangling is compiler dependent, you may not get exactly the same output with all compilers... For instance if you have a std::array<int, 10>
:
gcc: std::array<int, 10ul>
clang: std::__1::array<int, 10ul>
msvc: class std::array<int,10>
Note: The usage is "complicated" to support MSVC: Initially I used a lambda inside CREATE
so that you could do CREATE(Foo, x, y, z)()
without having to bother creating a variable (I do not know how to generate correct name - see initial version of this answer), but MSVC do not like decltype(std::make_tuple(x, y, z))
inside the lambda... (probably a bug).