Given a struct like this:
struct Foo
{
int x;
int y;
double z;
};
BOOST_FUSION_ADAPT_STRUCT(Foo, x, y, z);
I want to generate a string like this:
"{ int x; int y; double z; }"
I have seen how to print the values of a Fusion adapted struct, but here I need to print the types and names only.
How can I do this mostly simply? I'm not married to Boost.Fusion if there's a better way.
I think you can get something similar to what you want by making some slight modifications on the code in this answer. You can easily get the member name using boost::fusion::extension::struct_member_name
but, as far as I know, you can't directly get the member type name. You can get the member type using boost::fusion::result_of::value_at
(amongst other options) and I've chosen to use Boost.TypeIndex to get its name (in varying degrees of prettiness, depending on the compiler and the types in question). All of this is assuming that you actually need the Fusion adaptation, if you don't you can probably get a simpler approach that does only what you need.
Full Code
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);
}
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).
来源:https://stackoverflow.com/questions/39033097/boost-fusion-convert-adapted-struct-type-to-text