Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Examples using VMD functionality

Boost , Chapter 1. The Variadic Macro Data Library 1.9 , Chapter 1. The Variadic Macro Data Library 1.9

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Примеры использования библиотеки всегда очень личные. Любая библиотека, использующая макропрограммирование, может решить, какие макрообъекты необходимы на основе самой библиотеки, а затем решить, упрощает ли функциональность макробиблиотеки, такой как VMD. С этой целью представленные здесь примеры являются весьма произвольными и представляют собой лишь попытки проиллюстрировать возможное использование функциональных возможностей VMD, не слишком беспокоясь о том, имеют ли эти примеры какое-либо практическое полезное применение в реальных ситуациях программирования. Таким образом, в этих примерах я попытался представить макропрограммы, использующие функциональность VMD, а не полные решения данной практической задачи.

Switch macro

В C++ есть утверждение «переключатель», которое мы можем эмулировать в макропрограммировании с использованием VMD. Для макроэмуляции мы будем иметь в качестве параметров макро:

  1. Значение, которое может быть любым типом данных VMD, может разбирать.
  2. Это набор вызывающих ценностей. Они будут использоваться при вызове соответствующего макроса.
  3. Вариадные параметры, каждый из которых — кортежи. Каждый кортеж состоит из двух элементов: имя значения для соответствия и имя макроса для вызова. Для случая «по умолчанию» кортеж является единственным элементом, который является названием макроса для вызова. Это наши эквиваленты заявлениям «кейса» переключателя C++.

Макро выглядит так:

BOOST_VMD_SWITCH(value,calling_values,...)

Мы должны быть осторожны, чтобы не разбирать название нашего макроса, чтобы позвонить каким-либо образом, поскольку это несостоятельное условие для BOOST_VMD_IS_EMPTY и впоследствии для любого анализа входных данных, которые мы могли бы сделать. Вместо этого мы просто извлекаем вызывающее макро-имя и просто называем его, передавая вызывающие значения.

Наша обработка:

  1. Преобразуйте наши вариадные параметры в кортеж, так как доступ к элементам кортежа проще.
  2. Используйте цикл BOOST_PP_WHILE, чтобы найти соответствующее значение и извлечь из него вызывающий макрос. Мы будем использовать BOOST_VMD_EQUAL, чтобы найти соответствующее значение.
  3. Вызовите вызывающий макрос с вызывающими значениями, когда мы вернемся из цикла BOOST_PP_WHILE.

Вот наш код:

#include <boost/vmd/detail/setup.hpp>
#if BOOST_PP_VARIADICS
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/comparison/equal.hpp>
#include <boost/preprocessor/control/expr_iif.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/control/while.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/facilities/expand.hpp>
#include <boost/preprocessor/tuple/replace.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/variadic/to_tuple.hpp>
#include <boost/preprocessor/variadic/size.hpp>
#include <boost/vmd/equal.hpp>
#include <boost/vmd/identity.hpp>
#include <boost/vmd/is_empty.hpp>
/*
  State index into state values
*/
#define BOOST_VMD_SWITCH_STATE_ELEM_INDEX 2
#define BOOST_VMD_SWITCH_STATE_ELEM_DEFAULT 4
#define BOOST_VMD_SWITCH_STATE_ELEM_RESULT 5
/*
  Retrieve the state value, never changes
*/
#define BOOST_VMD_SWITCH_STATE_GET_VALUE(state) \
    BOOST_PP_TUPLE_ELEM(0,state) \
/**/
/*
  Retrieve the state tuple of values, never changes
*/
#define BOOST_VMD_SWITCH_STATE_GET_CHOICES(state) \
    BOOST_PP_TUPLE_ELEM(1,state) \
/**/
/*
  Retrieve the state index
*/
#define BOOST_VMD_SWITCH_STATE_GET_INDEX(state) \
    BOOST_PP_TUPLE_ELEM(2,state) \
/**/
/*
  Retrieve the state tuple of values size, never changes
*/
#define BOOST_VMD_SWITCH_STATE_GET_SIZE(state) \
    BOOST_PP_TUPLE_ELEM(3,state) \
/**/
/*
  Retrieve the state default tuple
*/
#define BOOST_VMD_SWITCH_STATE_GET_DEFAULT(state) \
    BOOST_PP_TUPLE_ELEM(4,state) \
/**/
/*
  Retrieve the state result tuple
*/
#define BOOST_VMD_SWITCH_STATE_GET_RESULT(state) \
    BOOST_PP_TUPLE_ELEM(5,state) \
/**/
/*
  Retrieve the current value tuple
*/
#define BOOST_VMD_SWITCH_STATE_GET_CURRENT_CHOICE(state) \
    BOOST_PP_TUPLE_ELEM \
        ( \
        BOOST_VMD_SWITCH_STATE_GET_INDEX(state), \
        BOOST_VMD_SWITCH_STATE_GET_CHOICES(state) \
        ) \
/**/
/*
  Expands to the state
  value = value to compare against
  tuple = choices as a tuple of values
  size = size of tuple of values
  None of these ever change in the WHILE state
*/
#define BOOST_VMD_SWITCH_STATE_EXPAND(value,tuple,size) \
    (value,tuple,0,size,(0,),(,)) \
/**/
/*
  Expands to the WHILE state
  The state to our WHILE consists of a tuple of elements:
  1: value to compare against
  2: tuple of values. Each value is a value/macro pair or if the default just a macro
  3: index into the values
  4: tuple for default macro. 0 means no default macro, 1 means default macro and then second value is the default macro.
  5: tuple of result matched. Emptiness means no result yet specified, 0 means no match, 1 means match and second value is the matching macro.
*/
#define BOOST_VMD_SWITCH_STATE(value,...) \
    BOOST_VMD_SWITCH_STATE_EXPAND \
        ( \
        value, \
        BOOST_PP_VARIADIC_TO_TUPLE(__VA_ARGS__), \
        BOOST_PP_VARIADIC_SIZE(__VA_ARGS__) \
        ) \
/**/
/*
  Sets the state upon a successful match.
  macro = is the matching macro found
*/
#define BOOST_VMD_SWITCH_OP_SUCCESS(d,state,macro) \
    BOOST_PP_TUPLE_REPLACE_D \
        ( \
        d, \
        state, \
        BOOST_VMD_SWITCH_STATE_ELEM_RESULT, \
        (1,macro) \
        ) \
/**/
/*
  Sets the state upon final failure to find a match.
  def = default tuple macro, ignored
*/
#define BOOST_VMD_SWITCH_OP_FAILURE(d,state,def) \
    BOOST_PP_TUPLE_REPLACE_D \
        ( \
        d, \
        state, \
        BOOST_VMD_SWITCH_STATE_ELEM_RESULT, \
        (0,) \
        ) \
/**/
/*
  Increments the state index into the tuple values
*/
#define BOOST_VMD_SWITCH_OP_UPDATE_INDEX(d,state) \
    BOOST_PP_TUPLE_REPLACE_D \
        ( \
        d, \
        state, \
        BOOST_VMD_SWITCH_STATE_ELEM_INDEX, \
        BOOST_PP_INC(BOOST_VMD_SWITCH_STATE_GET_INDEX(state)) \
        ) \
/**/
/*
  Choose our current value's macro as our successful match
  tuple = current tuple to test
*/
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE_MATCH(d,state,tuple) \
    BOOST_VMD_SWITCH_OP_SUCCESS(d,state,BOOST_PP_TUPLE_ELEM(1,tuple)) \
/**/
/*
  Update our state index
  tuple = current tuple to test, ignored
*/
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE_UPDATE_INDEX(d,state,tuple) \
    BOOST_VMD_SWITCH_OP_UPDATE_INDEX(d,state) \
/**/
/*
  Test our current value against our value to compare against
  tuple = current tuple to test
*/
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE(d,state,tuple) \
    BOOST_PP_IIF \
        ( \
        BOOST_VMD_EQUAL_D \
            ( \
            d, \
            BOOST_VMD_SWITCH_STATE_GET_VALUE(state), \
            BOOST_PP_TUPLE_ELEM(0,tuple) \
            ), \
        BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE_MATCH, \
        BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE_UPDATE_INDEX \
        ) \
    (d,state,tuple)    \
/**/
/*
  Set our default macro and update the index in our WHILE state
  tuple = current tuple to test
*/
#if BOOST_VMD_MSVC
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_CREATE_DEFAULT_NN(number,name) \
    (number,name) \
/**/
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_CREATE_DEFAULT(d,state,tuple) \
    BOOST_VMD_SWITCH_OP_UPDATE_INDEX \
        ( \
        d, \
        BOOST_PP_TUPLE_REPLACE_D \
            ( \
            d, \
            state, \
            BOOST_VMD_SWITCH_STATE_ELEM_DEFAULT, \
            BOOST_VMD_SWITCH_OP_TEST_CURRENT_CREATE_DEFAULT_NN(1,BOOST_PP_TUPLE_ENUM(tuple)) \
            ) \
        ) \
/**/
#else
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_CREATE_DEFAULT(d,state,tuple) \
    BOOST_VMD_SWITCH_OP_UPDATE_INDEX \
        ( \
        d, \
        BOOST_PP_TUPLE_REPLACE_D \
            ( \
            d, \
            state, \
            BOOST_VMD_SWITCH_STATE_ELEM_DEFAULT, \
            (1,BOOST_PP_TUPLE_ENUM(tuple)) \
            ) \
        ) \
/**/
#endif
/*
  If our current value is a default macro, just set the default macro,
  else test our current value.
  tuple = current tuple to test
*/
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_TUPLE(d,state,tuple) \
    BOOST_PP_IIF \
        ( \
        BOOST_PP_EQUAL_D \
            ( \
            d, \
            BOOST_PP_TUPLE_SIZE(tuple), \
            1 \
            ), \
        BOOST_VMD_SWITCH_OP_TEST_CURRENT_CREATE_DEFAULT, \
        BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE \
        ) \
    (d,state,tuple) \
/**/
/*
  Test the current value in our tuple of values
*/
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT(d,state) \
    BOOST_VMD_SWITCH_OP_TEST_CURRENT_TUPLE \
        ( \
        d, \
        state, \
        BOOST_VMD_SWITCH_STATE_GET_CURRENT_CHOICE(state) \
        ) \
/**/
/*
  Choose the default macro as our successful match
  def = default tuple consisting of just the default macro name
*/
#define BOOST_VMD_SWITCH_OP_DEFAULT_RET_CHOSEN(d,state,def) \
    BOOST_VMD_SWITCH_OP_SUCCESS \
        ( \
        d, \
        state, \
        BOOST_PP_TUPLE_ELEM(1,def) \
        ) \
/**/
/*
  If the default macro exists, choose it else indicate no macro was found
  def = default tuple consisting of just the default macro name
*/
#define BOOST_VMD_SWITCH_OP_DEFAULT_RET(d,state,def) \
    BOOST_PP_IIF \
        ( \
        BOOST_PP_TUPLE_ELEM(0,def), \
        BOOST_VMD_SWITCH_OP_DEFAULT_RET_CHOSEN, \
        BOOST_VMD_SWITCH_OP_FAILURE \
        ) \
    (d,state,def) \
/**/
/*
 Try to choose the default macro if it exists
*/
#define BOOST_VMD_SWITCH_OP_DEFAULT(d,state) \
    BOOST_VMD_SWITCH_OP_DEFAULT_RET \
        ( \
        d, \
        state, \
        BOOST_VMD_SWITCH_STATE_GET_DEFAULT(state) \
        ) \
/**/
/*
  WHILE loop operation
  Check for the next value match or try to choose the default if all matches have been checked
*/
#define BOOST_VMD_SWITCH_OP(d,state) \
    BOOST_PP_IIF \
        ( \
        BOOST_PP_EQUAL_D \
            (  \
            d, \
            BOOST_VMD_SWITCH_STATE_GET_INDEX(state), \
            BOOST_VMD_SWITCH_STATE_GET_SIZE(state) \
            ), \
        BOOST_VMD_SWITCH_OP_DEFAULT, \
        BOOST_VMD_SWITCH_OP_TEST_CURRENT \
        ) \
    (d,state) \
/**/
/*
  WHILE loop predicate
  Continue the WHILE loop if a result has not yet been specified
*/
#define BOOST_VMD_SWITCH_PRED(d,state) \
    BOOST_VMD_IS_EMPTY \
        ( \
        BOOST_PP_TUPLE_ELEM \
            ( \
            0, \
            BOOST_VMD_SWITCH_STATE_GET_RESULT(state) \
            ) \
        ) \
/**/
/*
  Invokes the function-like macro
  macro = function-like macro name
  tparams = tuple of macro parameters
*/
#define BOOST_VMD_SWITCH_PROCESS_INVOKE_MACRO(macro,tparams) \
    BOOST_PP_EXPAND(macro tparams) \
/**/
/*
  Processes our WHILE loop result
  callp = tuple of parameters for the called macro
  result = tuple. The first tuple element is 0
           if no macro has been found or 1 if a macro
           has been found. If 1 the second element is
           the name of a function-like macro
*/
#define BOOST_VMD_SWITCH_PROCESS(callp,result) \
    BOOST_PP_EXPR_IIF \
        ( \
        BOOST_PP_TUPLE_ELEM(0,result), \
        BOOST_VMD_SWITCH_PROCESS_INVOKE_MACRO \
            ( \
            BOOST_PP_TUPLE_ELEM(1,result), \
            callp \
            ) \
        ) \
/**/
/*
  Use BOOST_VMD_SWITCH_IDENTITY to pass a fixed value instead
  of a function-like macro as the second element of
  any tuple of the variadic parameters, or as the default
  value, to BOOST_VMD_SWITCH.
*/
#if BOOST_VMD_MSVC
#define BOOST_VMD_SWITCH_IDENTITY(item) BOOST_PP_CAT(BOOST_VMD_IDENTITY(item),)
#else
#define BOOST_VMD_SWITCH_IDENTITY BOOST_VMD_IDENTITY
#endif
/*
  Switch macro
  Parameters are:
  value = value to compare against. May be any VMD data value.
  callp = tuple of parameters for the called macro
  variadic parameters = each parameter must be a tuple.
    Each tuple consists of a two-element tuple. The first element is
    a value, which may be any VMD data value, and the second element
    is the name of a function-like macro to be called if the value
    is equal to the value to compare against. For a default value
    the tuple is a single-element tuple which contains the name of
    a function-like macro to be called if no other value matches.
*/
#define BOOST_VMD_SWITCH(value,callp,...) \
    BOOST_VMD_SWITCH_PROCESS \
        ( \
        callp, \
        BOOST_VMD_SWITCH_STATE_GET_RESULT \
            ( \
            BOOST_PP_WHILE \
                ( \
                BOOST_VMD_SWITCH_PRED, \
                BOOST_VMD_SWITCH_OP, \
                BOOST_VMD_SWITCH_STATE(value,__VA_ARGS__) \
                ) \
            ) \
        ) \
/**/
#endif /* BOOST_PP_VARIADICS */

Код довольно вовлечен, но он комментируется, чтобы его можно было понять. Есть несколько обходных путей для задачи препроцессора VC++, которую я обнаружил, связанную с передачей имени функционального макроса в кортеже.

Макрос BOOST_VMD_SWITCH может использоваться как с макросами для вызова, так и с фиксированными значениями для возврата. При указании макросов для вызова макроимя является вторым элементом соответствующего значения-макрокортежа, или в случае «по умолчанию» это просто само макроимя. При указании фиксированных значений для возврата макроимя является BOOST_VMD_SWITCH_IDENTITY(fixed_value), будь то в качестве второго элемента соответствующего значения-макромного кортежа или в качестве макроимя случая по умолчанию. В вариадных параметрах пользователь может смешивать макро-имена и фиксированные значения по своему усмотрению.

Несколько простых примеров:

#define BOOST_VMD_SWITCH_TEST_1(number) \
    test1_ ## number
/**/
#define BOOST_VMD_SWITCH_TEST_2(number) \
    test2_ ## number
/**/
#define BOOST_VMD_SWITCH_TEST_3(number) \
    test3_ ## number
/**/
#define BOOST_VMD_SWITCH_TEST_DEFAULT(number) \
    test_default_ ## number
/**/

Мы будем использовать эти простые макросы в наших вызовах BOOST_VMD_SWITCH.

BOOST_VMD_SWITCH(1,
                (7),
                (BOOST_VMD_SWITCH_TEST_DEFAULT),
                (3,BOOST_VMD_SWITCH_TEST_3),
                (1,BOOST_VMD_SWITCH_TEST_1),
                (2,BOOST_VMD_SWITCH_TEST_2)
                )

Здесь наш макрос вернет "test1_7".

Обратите внимание, что «случаи» могут быть в любом порядке.

BOOST_VMD_SWITCH(4,
                (7),
                (BOOST_VMD_SWITCH_TEST_DEFAULT),
                (2,BOOST_VMD_SWITCH_TEST_2),
                (1,BOOST_VMD_SWITCH_TEST_1),
                (3,BOOST_VMD_SWITCH_TEST_3)
                )

Здесь макрос использует случай по умолчанию и возвращает «test_default_7».

BOOST_VMD_SWITCH(143,
                (7),
                (BOOST_VMD_SWITCH_TEST_DEFAULT),
                (1,BOOST_VMD_SWITCH_TEST_1),
                (2,BOOST_VMD_SWITCH_TEST_2),
                (3,BOOST_VMD_SWITCH_TEST_3),
                (143,BOOST_VMD_SWITCH_IDENTITY(55))
                )

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

BOOST_VMD_SWITCH(155,
                (7),
                (BOOST_VMD_SWITCH_IDENTITY(77)),
                (1,BOOST_VMD_SWITCH_TEST_1),
                (2,BOOST_VMD_SWITCH_TEST_2),
                (3,BOOST_VMD_SWITCH_TEST_3),
                (143,BOOST_VMD_SWITCH_IDENTITY(55))
                )

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

BOOST_VMD_SWITCH(BOOST_VMD_TYPE_TUPLE,
                (7),
                (BOOST_VMD_SWITCH_TEST_DEFAULT),
                (BOOST_VMD_TYPE_TUPLE,BOOST_VMD_SWITCH_TEST_1),
                ((1,2,3),BOOST_VMD_SWITCH_TEST_3),
                (2,BOOST_VMD_SWITCH_TEST_2)
                )

Это показывает, что значения «значение» и каждое значение «случай» могут быть различными типами данных, если только типы являются теми, которые VMD может анализировать.

С кодом BOOST_VMD_SWITCH можно сделать еще больше, но я считаю, что он может быть полезен для программистов, пишущих макрокод. Например, нет никакой проверки того, что более одного «случайного» значения одинаково. Мы могли бы создать BOOST_VMD_ASSERT, если бы это была ситуация. Не существует концепции перехода к следующему «случайу», поскольку «разрыв» не используется в нижней части конкретного заявления «случай» C++. Тем не менее, пример дает макропрограммисту представление о том, что можно сделать с помощью макроса BOOST_VMD_EQUAL при обработке типов данных в целом, используя BOOST_VMD_IS_EMPTY для тестирования на пустоту и используя BOOST_VMD_IDENTITY для генерации фиксированного значения при совершении макровызова.

TTI inner template

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

Библиотека Boost TTI, разработчиком которой является VMD, определяет способ интроспектирования шаблона внутреннего класса класса. Интроспекция может происходить для шаблона внутреннего класса конкретных параметров шаблона.

В библиотеке используется макрос для создания метафункции, которая позволяет интроспекции работать. Используемый макрос называется BOOST_TTI_TEMPLATE. Макро имеет как вариадную, так и невариадную версию.

В невариадной версии макрос всегда берет два параметра для интроспекции конкретных параметров шаблона. Первый параметр - это название шаблона, а второй параметр - массив конкретных параметров шаблона (с самими именами параметров или без них). Итак, для шаблона класса формы:

template <class X,int Y> class MyTemplate { ... code };

Невариадный макрос будет:

BOOST_TTI_TEMPLATE(MyTemplate,(2,(class,int))) // uses array

Я выбрал массив Boost PP, а не Boost PP seq или список Boost PP, поскольку я чувствовал, что обозначение для определения параметров шаблона было ближе к массиву, чем к другим. Выбор кортежа Boost PP не был вариантом, поскольку для невариадных макросов нет способа автоматически узнать размер кортежа, поэтому предпочтительным был массив.

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

BOOST_TTI_TEMPLATE(MyTemplate,class,int) // uses variadic parameters

Так как это наиболее естественное обозначение.

Но для совместимости с невариадной версией конечный пользователь с вариадной макроподдержкой также может выбрать форму массива Boost PP выше.

Используя VMD, вариадная версия может поддерживать любой из других типов композитов Boost PP для конкретных параметров шаблона, хотя я считаю, что форма вариадных параметров проста в использовании. В этом сценарии пользователь может указать:

BOOST_TTI_TEMPLATE(MyTemplate,(class,(int,BOOST_PP_NIL))) // use a list

или

BOOST_TTI_TEMPLATE(MyTemplate,(class)(int)) // use a seq

или

BOOST_TTI_TEMPLATE(MyTemplate,(class,int)) // use a tuple

Единственное необходимое изменение будет в коде, который принимает второй параметр и преобразует его в конечную форму, используемую внутри. Это происходит в макро BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS. в файле. Код имеет две ситуации: одну для VC++8 или ниже и одну для всех других компиляторов. В нашем примере мы сосредоточимся только на одном для всех других компиляторов. Вам не нужно знать, что код делает внутри, чтобы завершить создание соответствующей метафункции. Макрокод, о котором идет речь, выглядит так:

#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \
  BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \
    ( \
      ( BOOST_PP_ADD(BOOST_PP_ARRAY_SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ARRAY_ENUM(tpArray) ) )  \
    ) \
/**/

В этом коде мы берем имя метафункции (признак), имя шаблона (название) и наши конкретные параметры шаблона (tpArray) и передаем информацию в виде массива Boost PP другому макросу, который в конечном итоге создаст метафункцию, которую конечный пользователь использует для проверки, существует ли такой шаблон класса в некотором закрытом классе. Даже если tpArray был списком, seq или tuple, мы все равно хотим передать информацию внутри BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE в форме, которую вы можете видеть выше. Нам не нужно и не нужно менять это внутреннее представление.

Текущий код, используемый как невариадной, так и вариадной версией шаблона BOOST_TTI_TEMPLATE, предполагает, что параметр tpArray представляет собой массив Boost PP. Но если это может быть кортеж, сек или список в вариадной версии, код может стать, с соответствующими файлами заголовков Boost PP и VMD:

#include <boost/preprocessor/arithmetic/add.hpp>
#include <boost/preprocessor/array/enum.hpp>
#include <boost/preprocessor/array/size.hpp>
#include <boost/preprocessor/control/expr_iif.hpp>
#include <boost/preprocessor/control/iif.hpp>
#include <boost/preprocessor/list/enum.hpp>
#include <boost/preprocessor/list/size.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/vmd/identity.hpp>
#include <boost/vmd/is_array.hpp>
#include <boost/vmd/is_list.hpp>
#include <boost/vmd/is_seq.hpp>
#include <boost/vmd/is_tuple.hpp>
#if BOOST_PP_VARIADICS
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \
  BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \
    ( \
      BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT \
        ( \
        trait,name,tpArray, \
        BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE(tpArray) \
        ) \
    ) \
/**/
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE(tpArray) \
  BOOST_VMD_IDENTITY_RESULT \
    ( \
      BOOST_PP_IIF \
        ( \
        BOOST_VMD_IS_ARRAY(tpArray), \
        BOOST_VMD_IDENTITY(ARRAY), \
        BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_LIST \
        ) \
      (tpArray) \
    ) \
/**/
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_LIST(tpArray) \
  BOOST_VMD_IDENTITY_RESULT \
    ( \
      BOOST_PP_IIF \
        ( \
        BOOST_VMD_IS_LIST(tpArray), \
        BOOST_VMD_IDENTITY(LIST), \
        BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_SEQ \
        ) \
      (tpArray) \
    ) \
/**/
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_SEQ(tpArray) \
  BOOST_VMD_IDENTITY_RESULT \
    ( \
      BOOST_PP_IIF \
        ( \
        BOOST_VMD_IS_SEQ(tpArray), \
        BOOST_VMD_IDENTITY(SEQ), \
        BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_TUPLE \
        ) \
      (tpArray) \
    ) \
/**/
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_TUPLE(tpArray) \
  BOOST_VMD_IDENTITY_RESULT \
    ( \
      BOOST_PP_EXPR_IIF \
        ( \
        BOOST_VMD_IS_TUPLE(tpArray), \
        BOOST_VMD_IDENTITY(TUPLE) \
        ) \
    ) \
/**/
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT(trait,name,tpArray,name) \
  ( BOOST_PP_ADD(BOOST_PP_ ## name ## _SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ ## name ## _ENUM(tpArray) ) )  \
/**/
#else
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \
  BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \
    ( \
      ( BOOST_PP_ADD(BOOST_PP_ARRAY_SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ARRAY_ENUM(tpArray) ) )  \
    ) \
/**/
#endif

Это, конечно, становится более сложным, но может быть значительно сокращено, если мы предпочтем использовать BOOST_VMD_GET_TYPE и изобретенный BOOST_VMD_SWITCH нашего первого примера. В этой второй версии кода мы предположим, что наш макрос BOOST_VMD_SWITCH был куда-то включен.

#include <boost/preprocessor/arithmetic/add.hpp>
#include <boost/preprocessor/array/enum.hpp>
#include <boost/preprocessor/array/size.hpp>
#include <boost/preprocessor/list/enum.hpp>
#include <boost/preprocessor/list/size.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/vmd/get_type.hpp>
#if BOOST_PP_VARIADICS
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \
  BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \
    ( \
      BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT \
        ( \
        trait,name,tpArray, \
        BOOST_VMD_SWITCH \
          ( \
          BOOST_VMD_GET_TYPE(tpArray), \
          (1), \
          (BOOST_VMD_TYPE_ARRAY,BOOST_VMD_SWITCH_IDENTITY(ARRAY)), \
          (BOOST_VMD_TYPE_LIST,BOOST_VMD_SWITCH_IDENTITY(LIST)), \
          (BOOST_VMD_TYPE_SEQ,BOOST_VMD_SWITCH_IDENTITY(SEQ)), \
          (BOOST_VMD_TYPE_TUPLE,BOOST_VMD_SWITCH_IDENTITY(TUPLE)) \
          ) \
        ) \
    ) \
/**/
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT(trait,name,tpArray,name) \
  ( BOOST_PP_ADD(BOOST_PP_ ## name ## _SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ ## name ## _ENUM(tpArray) ) )  \
/**/
#else
#define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \
  BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \
    ( \
      ( BOOST_PP_ADD(BOOST_PP_ARRAY_SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ARRAY_ENUM(tpArray) ) )  \
    ) \
/**/
#endif

Это короче и легче понять. Значения «(1)», передаваемые в BOOST_VMD_SWITCH, также могут быть «()», но VC8 имеет проблемы с пустыми скобками.

В случае с TTI, стоит ли такое изменение давать больше гибкости конечному пользователю? На самом деле, поскольку вариадная версия передачи конкретных параметров шаблона в виде вариадных данных синтаксически проще в использовании, чем любая из композитных форм Boost PP, я на самом деле достаточно доволен тем, что использование не преследует функциональность, которую я представил в этом примере. Но пример, тем не менее, показывает силу функциональности VMD для создания макросов, которые добавляют гибкость, когда макропрограммист чувствует, что он нуждается в ней для своей библиотеки.


PrevUpHomeNext

Статья Examples using VMD functionality раздела Chapter 1. The Variadic Macro Data Library 1.9 Chapter 1. The Variadic Macro Data Library 1.9 может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 1. The Variadic Macro Data Library 1.9 ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 18:43:35/0.0077471733093262/1