Boost Fusion: преобразование адаптированного типа структуры в текст

Учитывая такую ​​структуру:

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, если есть лучший способ.

3

Решение

Я думаю, что вы можете получить что-то похожее на то, что вы хотите, сделав некоторые небольшие изменения в коде в этот ответ. Вы можете легко получить имя участника, используя 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);
}
4

Другие решения

Вы можете использовать следующее решение, которое зависит от компилятора (проверено на 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)) внутри лямбды … (вероятно, ошибка).

0

По вопросам рекламы [email protected]