Использование препроцессора boost для итеративного вызова шаблона с переменным числом аргументов

Предположим, у меня есть вариативный шаблон:

template<typename... Args>
class Foo;

Этот вариативный шаблон рекурсивно генерирует другой шаблон, пока не достигнет одного аргумента Foo на последнем уровне. Теперь я хочу иметь макрос, например, Bar(...), который, когда я его вызываю, я получаю что-то вроде этого:

Bar(float, int, string, vector<int>)
// expands to
Macro(Foo<float, int, string, vector<int>>)
Macro(Foo<int, string, vector<int>>)
Macro(Foo<string, vector<int>>)
Macro(Foo<vector<int>>)

Который Macro(...) является еще одним макросом для выполнения чего-либо в этом классе. Я надеюсь, что смогу использовать препроцессор Boost для сокращения кода, который мне нужно написать.

Пожалуйста, предложите мне несколько советов, которые помогут мне написать такой макрос.


person motam    schedule 27.01.2016    source источник
comment
Это возможно, конечно, но это серьезно похоже на XY-проблему.   -  person Rumburak    schedule 27.01.2016
comment
Являются ли Bar и Macro разными макросами? И вы хотите, чтобы макрос Macro вызывался для каждого расширения вызова вариативного шаблона?   -  person RobClucas    schedule 27.01.2016
comment
Спасибо за совет. Хотя я не был полностью согласен с вами, но я изменил вопрос, чтобы не смотреть на проблему XY.   -  person motam    schedule 27.01.2016
comment
@nabla Ответ на оба ваших вопроса - да. Они разные, и Macro должен вызываться для каждой версии.   -  person motam    schedule 27.01.2016
comment
Это работает, но я сомневаюсь, что это лучший способ решить эту проблему. Добавление тега boost-preprocessor может помочь вашему ответу дойти до эксперта в библиотеке.   -  person llonesmiz    schedule 27.01.2016
comment
Спасибо за ваш совет и решение. В настоящее время я не могу проверить это в своем основном проекте, но, похоже, это работает. Я проверю это в ближайшее время и расскажу вам о решении.   -  person motam    schedule 27.01.2016
comment
@MohsenTamiz Я думаю, что это небольшое изменение имеет немного больше смысла.   -  person llonesmiz    schedule 28.01.2016
comment
Извините за опоздание, у меня не было доступа к моим исходным кодам. Я проверил ваше решение, и оно работает очень хорошо. Спасибо за ваше решение. Если бы вы написали это как ответ, я мог бы проверить это как принятый ответ.   -  person motam    schedule 30.01.2016


Ответы (2)


Я не знаю, лучший ли это подход к решению вашей проблемы, но это делает то, что вы хотите:

#include <boost/preprocessor/repetition/repeat_from_to.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define CALL_MACRO(_,__,SEQ) Macro(Foo<BOOST_PP_SEQ_ENUM(SEQ)>)

#define GENERATE_MACRO_INVOCATIONS(SEQS) BOOST_PP_SEQ_FOR_EACH(CALL_MACRO,_,SEQS)

#define GENERATE_DESCENDING_SEQUENCES(_,INDEX,DATA) (BOOST_PP_SEQ_REST_N(INDEX,DATA))

#define BAR_IMPL(SEQ) GENERATE_MACRO_INVOCATIONS(BOOST_PP_REPEAT_FROM_TO(0,BOOST_PP_SEQ_SIZE(SEQ),GENERATE_DESCENDING_SEQUENCES, SEQ))


#define Bar(...) BAR_IMPL(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

Bar(float, int, string, vector<int>)
  • У вас изначально есть переменные данные: float, int, string, vector<int>.
  • BOOST_PP_VARIADIC_TO_SEQ преобразует его в: (float)(int)(string)(vector<int>)
  • BOOST_PP_REPEAT_FROM_TO вызывает макрос GENERATE_DESCENDING_SEQUENCES BOOST_PP_SEQ_SIZE(SEQ) раз с последовательностью в качестве данных и индексом, начинающимся с 0.
  • BOOST_PP_SEQ_REST_N(INDEX,DATA) удаляет INDEX первых элементов из DATA и возвращает остальные. Этот результат заключен в пару круглых скобок.
  • После вызова REPEAT у вас есть последовательность последовательностей:

    ((float)(int)(string)(vector))((int)(string)(vector))((string) (вектор))((вектор))

  • BOOST_PP_SEQ_FOR_EACH вызывает CALL_MACRO с каждым элементом в последовательности последовательностей.

  • И, наконец, BOOST_PP_SEQ_ENUM берет последовательность и возвращает ее элементы, разделенные запятыми.

Предварительно обработано на Coliru

person Community    schedule 01.02.2016

Обновление: отредактированный ответ на основе комментариев. Теперь вызывает макрос BOOST_EXPORT_CLASS вместо функции macro(), которую я определил. Я не проверял это с BOOST_EXPORT_CLASS, однако я провел симуляцию, обращаясь к типу Foo<...> на каждом уровне расширения шаблона. Я смог получить доступ к каждому из разных типов для разных расширений, поэтому я предполагаю, что все, что делает BOOST_EXPORT_CLASS, должно работать.

Я думаю, что теперь это делает то, что вы хотите:

#define BAR(...) Foo<__VA_ARGS__>()

// Variadic definition of Foo
template <typename... Args>
struct Foo;

// Specialize Foo for the case that there is at least on template parameter
template <typename Arg, typename... Args>
struct Foo<Arg, Args...> : Foo<Args...> {
  using type = Foo<Arg, Args...>;
  Foo() : Foo<Args...>(){
    BOOST_EXPORT_CLASS(type)
  }
};

// Terminating case for Foo
template <>
struct Foo<> {
  using type = Foo<>;
  Foo() { BOOST_EXPORT_CLASS(type) }
};

int main() {
  BAR(int, float, double);
}

Чтобы проверить, что это теоретически должно работать, я определил следующий макрос:

#define MACRO(x) test<x>();

И заменил BOOST_EXPORT_CLASS(type) на MACRO(type). Функция test следующая:

template <typename T>
void test() {
  std::cout << typeid(T).name() << "\n";
}

Запуск кода напечатал следующее (я добавил комментарии), которое показывает, что шаблоны расширены в макросе и должны отображаться как BOOST_EXPORT_CLASS, как показано в вопросе:

3FooIJEE      // Foo<>
3FooIJdEE     // Foo<double>
3FooIJfdEE    // Foo<float, double>
3FooIJifdEE   // Foo<int, float, double>

Вот живая демонстрация кода Живая демонстрация.

Следует отметить, что расширение идет в направлении, противоположном указанному в OP, то есть:

Macro(Foo<>)
Macro(Foo<double>)
Macro(Foo<float, double>)
Macro(Foo<int, float, double>)
person RobClucas    schedule 27.01.2016
comment
Может быть, я должен рассказать больше о Macro, это еще один макрос, который я должен использовать. В частности, это макрос BOOST_EXPORT_CLASS, который я хочу использовать для экспорта каждого типа класса. Поэтому я думаю, что ваш ответ недействителен, это шаблон и не то, что я хотел. - person motam; 27.01.2016
comment
@nabla Вы предполагаете, что Macro - это то, что можно свести к функции (constexpr или иначе), что, похоже, не относится к ОП. Например, Macro может расширяться до объявления. - person Elektito; 27.01.2016
comment
@MohsenTamiz, извините, я сделал предположение, основываясь на том, что понял из вопроса. Пожалуйста, смотрите обновленный ответ - надеюсь, это делает то, что вы хотите. Поскольку я не пробовал это с повышением, пожалуйста, дайте мне знать, если это сработает. - person RobClucas; 27.01.2016
comment
@Elektito, да, это была моя ошибка. Пожалуйста, посмотрите обновленный ответ - я думаю, что это делает то, что хочет ОП. - person RobClucas; 27.01.2016
comment
@nabla Спасибо за ваши соображения. Но все же я думаю, что ваше решение не является правильным. Поскольку вы вызываете макрос внутри конструктора, макрос, с которым я работал, является макросом, который объявляет другие классы, поэтому он никогда не сможет вызвать внутри конструктора. Ваше решение действительно только тогда, когда мы хотим сделать это с помощью функции, а не макроса для объявления. - person motam; 27.01.2016
comment
@MohsenTamiz Я не согласен, это должно делать именно то, о чем вы просили. Я не понимаю, почему важно, чтобы макрос вызывался в конструкторе. - person RobClucas; 27.01.2016
comment
Предположим, что этот макрос объявляет новый класс (например, #define Macro(name) class name{}), поэтому в этой ситуации он не может быть внутри конструктора. - person motam; 28.01.2016