Учитывая такую структуру:
struct Foo
{
int x;
int y;
double z;
};
BOOST_FUSION_ADAPT_STRUCT(Foo, x, y, z);
Я хочу сгенерировать такую строку:
"{ int x; int y; double z; }"
Я видел как распечатать ценности адаптированной структуры Fusion, но здесь мне нужно печатать только типы и имена.
Как я могу сделать это в основном просто? Я не женат на Boost.Fusion, если есть лучший способ.
Я думаю, что вы можете получить что-то похожее на то, что вы хотите, сделав некоторые небольшие изменения в коде в этот ответ. Вы можете легко получить имя участника, используя boost::fusion::extension::struct_member_name
но, насколько я знаю, вы не можете напрямую получить имя типа члена. Вы можете получить тип члена, используя boost::fusion::result_of::value_at
(среди других вариантов), и я решил использовать Boost.TypeIndex, чтобы получить его имя (в различной степени красивости, в зависимости от компилятора и рассматриваемых типов). Все это предполагает, что вам действительно нужна адаптация Fusion, а если нет, вы, вероятно, можете получить более простой подход, который делает только то, что вам нужно.
Полный код
Работает на WandBox (GCC)
Работает на 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);
}
Вы можете использовать следующее решение, которое зависит от компилятора (проверено на 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
извлекает std::type_info::name
каждого элемента из tuple
и объединяет его с именами атрибутов (например, "x, y, z"
).
CREATE
макрос возвращает лямбду, которую вы можете использовать следующим образом:
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
чтобы ты мог сделать CREATE(Foo, x, y, z)()
без необходимости создавать переменную (я не знаю, как создать правильное имя — см. первоначальную версию этого ответа), но MSVC не нравится decltype(std::make_tuple(x, y, z))
внутри лямбды … (вероятно, ошибка).