BOOST_PP_ITERATION для аргументов переменной длины

Я хочу включить luabind в один из моих проектов. Для этого мне нужно предоставить функцию, которая ведет себя аналогично call_function (см. ниже). Эта функция использует некоторую магию шаблонов (любезно предоставленную Boost), с которой я был бы признателен за помощь. Это первый раз, когда я действительно столкнулся с метапрограммированием шаблонов (это так называется?), и поэтому я немного растерялся. Вот несколько фрагментов, с которыми я был бы признателен за помощь.

#define LUABIND_TUPLE_PARAMS(z, n, data) const A##n *
#define LUABIND_OPERATOR_PARAMS(z, n, data) const A##n & a##n

Я не совсем уверен, что делает этот бит препроцессора, я даже не знаю, как он называется, поэтому поиск немного затруднен. A — это тип шаблона. Если я правильно помню, #a будет вставлять буквальный текст a, но что делают кратные #? После этого препроцессора идет вот это.

template<class Ret BOOST_PP_COMMA_IF(BOOST_PP_ITERATION()) BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(), class A)>
    typename boost::mpl::if_<boost::is_void<Ret>
            , luabind::detail::proxy_function_void_caller<boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> >
            , luabind::detail::proxy_function_caller<Ret, boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> > >::type
    call_function(lua_State* L, const char* name BOOST_PP_COMMA_IF(BOOST_PP_ITERATION()) BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_OPERATOR_PARAMS, _) )
    {
        typedef boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> tuple_t;
#if BOOST_PP_ITERATION() == 0
        tuple_t args;
#else
        tuple_t args(BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(), &a));
#endif

    }

Как видите, он интенсивно использует Boost. Я гуглил BOOST_PP_ITERATION, но до сих пор не могу понять, что он делает. Может кто-нибудь объяснить мне, желательно в контексте этого кода, что делает материал BOOST_PP и как ему удается получить аргументы в args.

Моя конечная цель — определить call_function в моем собственном коде, который будет генерировать args, который я могу передать перегрузке call_function, которую я определю. Это означает, что я могу использовать то же соглашение о вызовах, но также могу применить некоторую предварительную обработку перед вызовом luabind.

Этот вопрос довольно специфичен в том виде, в котором я его сформулировал, но я надеюсь, что концепции достаточно общие, чтобы здесь все было в порядке.


person user1520427    schedule 11.01.2013    source источник
comment
Вы действительно должны посмотреть на Boost.Preprocessor документация   -  person TemplateRex    schedule 11.01.2013


Ответы (1)


BOOST_PP_* не связан с метапрограммированием шаблонов, это библиотека препроцессора. Как следует из названия, он работает с магией препроцессора, делая некоторые действительно умопомрачительные вещи для создания множества похожих шаблонов. В вашем случае это будет следующее:

//preprocessor iteration 0
template<class Ret>
    typename boost::mpl::if_<boost::is_void<Ret>
            , luabind::detail::proxy_function_void_caller<boost::tuples::tuple<> >
            , luabind::detail::proxy_function_caller<Ret, boost::tuples::tuple<> > >::type
    call_function(lua_State* L, const char* name )
    {
        typedef boost::tuples::tuple<> tuple_t;
        tuple_t args;
    }

//preprocessor iteration 1
template<class Ret , class A0>
typename boost::mpl::if_<boost::is_void<Ret>
        , luabind::detail::proxy_function_void_caller<boost::tuples::tuple<const A0 *> >
        , luabind::detail::proxy_function_caller<Ret, boost::tuples::tuple<const A0 *> > >::type
call_function(lua_State* L, const char* name , const A0 & a0 )
{
    typedef boost::tuples::tuple<const A0 *> tuple_t;
    tuple_t args(&a0);
}

и так далее, до некоторого максимума, определенного в другом месте (например, A0, A1, A2, A3... A9, если максимум равен 10)

## — это конкатенация токенов для препроцессора, в данном случае конкатенация A (или a) с любым значением n (=> A0, A1, A2, ...). Весь код находится в некотором цикле предварительной обработки.

  • BOOST_PP_ITERATION() дает индекс текущего цикла (0, 1, 2...)
  • BOOST_PP_COMMA_IF(X) ставит запятую, если аргумент не равен 0, например. запятая перед "классом A0" в итерации 1 в списке параметров шаблона
  • BOOST_PP_ENUM(n,B,C) дает разделенный запятыми список B(?, N, C), где N начинается от 0..(n-1), т. е. макрос B выполняется n раз, поэтому вызов BOOST_PP_ENUM(3, LUABIND_TUPLE_PARAMS, _) дает const A0 *, const A1 *, const A2 *
  • BOOST_PP_ENUM_PARAMS(n, X) дает список X##n, разделенный запятыми, например. &a0, &a1, &a2 для BOOST_PP_ENUM_PARAMS(3, &a)

Многие из вариантов использования этой магии препроцессора в наши дни могут быть реализованы с помощью вариативных шаблонов, поэтому, если вам повезет, вы больше не столкнетесь с этим материалом ;) Это нелегко понять с первого взгляда, потому что предварительная обработка не работает, как другие известные Возможности C++ и некоторые ограничения, которые необходимо обойти, делают его еще менее понятным.

person Arne Mertz    schedule 11.01.2013