Макросы с переменным числом аргументов: повторное использование аргументов с переменным числом аргументов (Boost.Fusion)

Я работаю над своего рода фреймворком "данные-переменные". Здесь структуры определены и адаптированы как последовательности Boost.Fusion с использованием макроса BOOST_FUSION_DEFINE_ASSOC_STRUCT.

Краткий контекст:

Чтобы определить две адаптированные структуры foo и bar, я использую:

#define VAR(VARNAME) vardefs::VARNAME
#define VAR_PARENTHESES(VARNAME) (vardefs)(VARNAME)

// ========================================================
#ifndef VARNAME_
#define VARNAME_ foo // <- Variable name
namespace VAR(VARNAME_)::keys{
    struct foo1;
    struct foo2;
    struct foo3;
}
namespace VAR(VARNAME_) { struct index; }

BOOST_FUSION_DEFINE_ASSOC_STRUCT(
    VAR_PARENTHESES(VARNAME_), type,
    (char, foo1, VAR(VARNAME_)::keys::foo1)
    (int, foo2, VAR(VARNAME_)::keys::foo2)
    (float, foo3, VAR(VARNAME_)::keys::foo3)
)
#endif
#undef VARNAME_
// --------------------------------------------------------

// ========================================================
#ifndef VARNAME_
#define VARNAME_ bar // <- Variable name
namespace VAR(VARNAME_)::keys{
    struct bar1;
    struct bar2;
}
namespace VAR(VARNAME_) { struct index; }

BOOST_FUSION_DEFINE_ASSOC_STRUCT(
    VAR_PARENTHESES(VARNAME_), type,
    (double, bar1, VAR(VARNAME_)::keys::bar1)
    (float, bar2, VAR(VARNAME_)::keys::bar2)
)
#endif
#undef VARNAME_
// --------------------------------------------------------

Теперь можно создавать экземпляры foo и bar, используя:

VAR(foo)::type fooI;
VAR(bar)::type barI;

Ассоциативные ключи можно использовать так:

auto a = boost::fusion::at_key<VAR(foo)::keys::foo3>(fooI).
auto b = boost::fusion::at_key<VAR(bar)::keys::bar2>(barI).

Этот подход может быть полезен в будущем.

Наконец, есть теги для самих структур:

VAR(bar)::index
VAR(foo)::index

Я могу использовать более позднее как типовые ключи в других последовательностях Boost.Fusion, например. boost::fusion::map.

Вопрос:

Обратите внимание, что для каждой из двух структур, и поскольку я определяю ассоциативную последовательность, я должен использовать имена полей структуры (foo1, bar2, ...) по три раза каждое.

Вместо этого я хотел бы определить две приведенные выше структуры следующим образом:

// ========================================================
DEFINE_DATAVAR_STRUCT(
    foo,
    (char, foo1)
    (int, foo2)
    (float, foo3)
)
// --------------------------------------------------------

// ========================================================
DEFINE_DATAVAR_STRUCT(
    bar,
    (double, bar1)
    (float, bar2)
)
// --------------------------------------------------------

Мне нужно только определить вариативный макрос DEFINE_DATAVAR_STRUCT. Вот для чего мне нужна помощь. Проблема для меня в том, чтобы повторно использовать вариативные аргументы, чтобы они могли появляться не только в сгенерированном коде. Каким-то образом Boost.Fusion делает это без проблем (полагаясь на Boost.PP).

Мое собственное исследование:

Я просмотрел библиотеку Boost.PP, но у меня не очень хороший прогресс. Поскольку библиотека Boost.Fusion уже делает это, должен быть способ достичь той же вариативной функциональности, подобной макросу BOOST_FUSION_DEFINE_ASSOC_STRUCT.

Макрос Boost.Fusion BOOST_FUSION_DEFINE_ASSOC_STRUCT определен в строках 40-50 в ... / define_assoc_struct.hpp. Кажется, большая часть волшебства для создания этих макросов находится в ... / define_struct.hpp (особенно макрос BOOST_FUSION_DEFINE_STRUCT_IMPL в строке 413). Этот файл также использует множество преимуществ Boost.PP.

Компилятор: Microsoft Visual Studio 2015 с обновлением 3 (GCC, Clang сейчас недоступны).

Boost: 1.64.0.


person Ole Thomsen Buus    schedule 05.08.2017    source источник
comment
Я бы просто сгенерировал код или переделал существующий макрос BOOST_FUSION_DEFINE_STRUCT (скопировал его и заставил вместо этого выдавать желаемый вами код)   -  person sehe    schedule 05.08.2017
comment
@sehe Это отличная идея. Именно то, что я имел в виду на самом деле. Моя проблема заключается в использовании __VA_ARGS__, так что я могу получить такое же вариативное поведение, что и макрос Boost.Fusion. Возможно, я могу просто скопировать реализацию Boost.Fusion, не уверен. Я немного обновлю вопрос.   -  person Ole Thomsen Buus    schedule 05.08.2017


Ответы (1)


Относительно легко создать макрос, который генерирует весь ваш шаблон из вызова предпочитаемого вами макроса:

DEFINE_DATAVAR_STRUCT(
    bar,
    (double, bar1)
    (float, bar2)
)

В этом макросе у вас есть два аргумента: имя (bar) и «последовательность» кортежей ((double,bar1)(float,bar2)). Я заключил «последовательность» в кавычки, потому что для того, чтобы иметь последовательность кортежей, которая может работать с макросами Boost.Preprocessor, вам нужно, чтобы каждый кортеж был разделен двумя наборами круглых скобок. В приведенном ниже примере большая часть сложности вызвана решением этой проблемы (дополнительную информацию об этом можно найти в подходе 3 в этом ответе < / а>.


Запуск на Wandbox

#include <iostream>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/fusion/include/define_assoc_struct.hpp>
#include <boost/fusion/include/at_key.hpp>



#define VAR(VARNAME) vardefs::VARNAME
#define VAR_PARENTHESES(VARNAME) (vardefs)(VARNAME)

//THIS IS ONLY NEEDED IN ORDER TO GET 2 SETS OF PARENTHESES
//Heavily "inspired" from BOOST_FUSION_ADAPT_STRUCT
#define GENERATE_DATAVAR_SEQUENCE_FILLER_0(X, Y)  \
    ((X, Y)) GENERATE_DATAVAR_SEQUENCE_FILLER_1
#define GENERATE_DATAVAR_SEQUENCE_FILLER_1(X, Y)  \
    ((X, Y)) GENERATE_DATAVAR_SEQUENCE_FILLER_0
#define GENERATE_DATAVAR_SEQUENCE_FILLER_0_END
#define GENERATE_DATAVAR_SEQUENCE_FILLER_1_END

#define GENERATE_DATAVAR_SEQUENCE(MEMBERS) BOOST_PP_CAT(GENERATE_DATAVAR_SEQUENCE_FILLER_0 MEMBERS,_END)

//THESE AREN'T ACTUALLY REQUIRED BUT HELP WITH READABILITY
#define DATAVAR_GET_TYPE(TUPLE) BOOST_PP_TUPLE_ELEM(2,0,TUPLE)
#define DATAVAR_GET_NAME(TUPLE) BOOST_PP_TUPLE_ELEM(2,1,TUPLE)

//THESE ARE THE HELPERS THAT ACTUALLY GENERATE THE VARIABLE PARTS OF THE MACRO
#define GENERATE_STRUCT_KEYS(_,__,TUPLE) struct DATAVAR_GET_NAME(TUPLE);
#define GENERATE_STRUCT_DEFINITION_SEQ(_,NAME,TUPLE) (DATAVAR_GET_TYPE(TUPLE),DATAVAR_GET_NAME(TUPLE),VAR(NAME)::keys::DATAVAR_GET_NAME(TUPLE))

// ===============================================================================================
#define DEFINE_DATAVAR_STRUCT(NAME,MEMBERS)                                                      \
namespace VAR(NAME)::keys{                                                                       \
    BOOST_PP_SEQ_FOR_EACH(GENERATE_STRUCT_KEYS,_,GENERATE_DATAVAR_SEQUENCE(MEMBERS))             \
}                                                                                                \
namespace VAR(NAME) { struct index; }                                                            \
BOOST_FUSION_DEFINE_ASSOC_STRUCT(                                                                \
    VAR_PARENTHESES(NAME), type,                                                                 \
    BOOST_PP_SEQ_FOR_EACH(GENERATE_STRUCT_DEFINITION_SEQ,NAME,GENERATE_DATAVAR_SEQUENCE(MEMBERS))\
)
// -----------------------------------------------------------------------------------------------


// ========================================================
DEFINE_DATAVAR_STRUCT(
    foo,
    (char, foo1)
    (int, foo2)
    (float, foo3)
)
// --------------------------------------------------------

// ========================================================
DEFINE_DATAVAR_STRUCT(
    bar,
    (double, bar1)
    (float, bar2)
)
// --------------------------------------------------------


int main()
{
    VAR(foo)::type fooI{'a',1,2.0f};
    VAR(bar)::type barI{1.0,2.0f};

    std::cout << boost::fusion::at_key<VAR(foo)::keys::foo1>(fooI) << std::endl;
    std::cout << boost::fusion::at_key<VAR(foo)::keys::foo2>(fooI) << std::endl;
    std::cout << boost::fusion::at_key<VAR(foo)::keys::foo3>(fooI) << std::endl;
    std::cout << boost::fusion::at_key<VAR(bar)::keys::bar1>(barI) << std::endl;
    std::cout << boost::fusion::at_key<VAR(bar)::keys::bar2>(barI) << std::endl;
}
person Community    schedule 08.08.2017
comment
Именно то, что мне нужно. Спасибо! Я знал, что это, вероятно, можно было бы сделать относительно легко, так как Boost.Fusion делает это. Я планировал сам погрузиться в макросы препроцессора. Я выбрал решение без ключей для структурных полей, но теперь я могу добавить полное решение. Здорово! - person Ole Thomsen Buus; 08.08.2017
comment
Если вы чего-то не понимаете, не стесняйтесь спрашивать. Когда у меня появляется больше времени, я стараюсь лучше (на самом деле вообще) объяснить, что я сделал. - person llonesmiz; 08.08.2017