Примеры использования библиотеки всегда очень личные. Любая библиотека, использующая макропрограммирование, может решить, какие макрообъекты необходимы на основе самой библиотеки, а затем решить, упрощает ли функциональность макробиблиотеки, такой как VMD. С этой целью представленные здесь примеры являются весьма произвольными и представляют собой лишь попытки проиллюстрировать возможное использование функциональных возможностей VMD, не слишком беспокоясь о том, имеют ли эти примеры какое-либо практическое полезное применение в реальных ситуациях программирования. Таким образом, в этих примерах я попытался представить макропрограммы, использующие функциональность VMD, а не полные решения данной практической задачи.
В C++ есть утверждение «переключатель», которое мы можем эмулировать в макропрограммировании с использованием VMD. Для макроэмуляции мы будем иметь в качестве параметров макро:
- Значение, которое может быть любым типом данных VMD, может разбирать.
- Это набор вызывающих ценностей. Они будут использоваться при вызове соответствующего макроса.
- Вариадные параметры, каждый из которых — кортежи. Каждый кортеж состоит из двух элементов: имя значения для соответствия и имя макроса для вызова. Для случая «по умолчанию» кортеж является единственным элементом, который является названием макроса для вызова. Это наши эквиваленты заявлениям «кейса» переключателя C++.
Макро выглядит так:
BOOST_VMD_SWITCH(value,calling_values,...)
Мы должны быть осторожны, чтобы не разбирать название нашего макроса, чтобы позвонить каким-либо образом, поскольку это несостоятельное условие для BOOST_VMD_IS_EMPTY и впоследствии для любого анализа входных данных, которые мы могли бы сделать. Вместо этого мы просто извлекаем вызывающее макро-имя и просто называем его, передавая вызывающие значения.
Наша обработка:
- Преобразуйте наши вариадные параметры в кортеж, так как доступ к элементам кортежа проще.
- Используйте цикл BOOST_PP_WHILE, чтобы найти соответствующее значение и извлечь из него вызывающий макрос. Мы будем использовать BOOST_VMD_EQUAL, чтобы найти соответствующее значение.
- Вызовите вызывающий макрос с вызывающими значениями, когда мы вернемся из цикла 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>
#define BOOST_VMD_SWITCH_STATE_ELEM_INDEX 2
#define BOOST_VMD_SWITCH_STATE_ELEM_DEFAULT 4
#define BOOST_VMD_SWITCH_STATE_ELEM_RESULT 5
#define BOOST_VMD_SWITCH_STATE_GET_VALUE(state) \
BOOST_PP_TUPLE_ELEM(0,state) \
#define BOOST_VMD_SWITCH_STATE_GET_CHOICES(state) \
BOOST_PP_TUPLE_ELEM(1,state) \
#define BOOST_VMD_SWITCH_STATE_GET_INDEX(state) \
BOOST_PP_TUPLE_ELEM(2,state) \
#define BOOST_VMD_SWITCH_STATE_GET_SIZE(state) \
BOOST_PP_TUPLE_ELEM(3,state) \
#define BOOST_VMD_SWITCH_STATE_GET_DEFAULT(state) \
BOOST_PP_TUPLE_ELEM(4,state) \
#define BOOST_VMD_SWITCH_STATE_GET_RESULT(state) \
BOOST_PP_TUPLE_ELEM(5,state) \
#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) \
) \
#define BOOST_VMD_SWITCH_STATE_EXPAND(value,tuple,size) \
(value,tuple,0,size,(0,),(,)) \
#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__) \
) \
#define BOOST_VMD_SWITCH_OP_SUCCESS(d,state,macro) \
BOOST_PP_TUPLE_REPLACE_D \
( \
d, \
state, \
BOOST_VMD_SWITCH_STATE_ELEM_RESULT, \
(1,macro) \
) \
#define BOOST_VMD_SWITCH_OP_FAILURE(d,state,def) \
BOOST_PP_TUPLE_REPLACE_D \
( \
d, \
state, \
BOOST_VMD_SWITCH_STATE_ELEM_RESULT, \
(0,) \
) \
#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)) \
) \
#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)) \
#define BOOST_VMD_SWITCH_OP_TEST_CURRENT_VALUE_UPDATE_INDEX(d,state,tuple) \
BOOST_VMD_SWITCH_OP_UPDATE_INDEX(d,state) \
#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) \
#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
#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) \
#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) \
) \
#define BOOST_VMD_SWITCH_OP_DEFAULT_RET_CHOSEN(d,state,def) \
BOOST_VMD_SWITCH_OP_SUCCESS \
( \
d, \
state, \
BOOST_PP_TUPLE_ELEM(1,def) \
) \
#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) \
#define BOOST_VMD_SWITCH_OP_DEFAULT(d,state) \
BOOST_VMD_SWITCH_OP_DEFAULT_RET \
( \
d, \
state, \
BOOST_VMD_SWITCH_STATE_GET_DEFAULT(state) \
) \
#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) \
#define BOOST_VMD_SWITCH_PRED(d,state) \
BOOST_VMD_IS_EMPTY \
( \
BOOST_PP_TUPLE_ELEM \
( \
0, \
BOOST_VMD_SWITCH_STATE_GET_RESULT(state) \
) \
) \
#define BOOST_VMD_SWITCH_PROCESS_INVOKE_MACRO(macro,tparams) \
BOOST_PP_EXPAND(macro tparams) \
#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 \
) \
) \
#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
#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
Код довольно вовлечен, но он комментируется, чтобы его можно было понять. Есть несколько обходных путей для задачи препроцессора 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 для генерации фиксированного значения при совершении макровызова.
В качестве более практического примера, просто чтобы показать возможное использование функциональности 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)))
Я выбрал массив Boost PP, а не Boost PP seq или список Boost PP, поскольку я чувствовал, что обозначение для определения параметров шаблона было ближе к массиву, чем к другим. Выбор кортежа Boost PP не был вариантом, поскольку для невариадных макросов нет способа автоматически узнать размер кортежа, поэтому предпочтительным был массив.
Для вариадной версии используются вариадные параметры, поэтому обозначение будет:
BOOST_TTI_TEMPLATE(MyTemplate,class,int)
Так как это наиболее естественное обозначение.
Но для совместимости с невариадной версией конечный пользователь с вариадной макроподдержкой также может выбрать форму массива Boost PP выше.
Используя VMD, вариадная версия может поддерживать любой из других типов композитов Boost PP для конкретных параметров шаблона, хотя я считаю, что форма вариадных параметров проста в использовании. В этом сценарии пользователь может указать:
BOOST_TTI_TEMPLATE(MyTemplate,(class,(int,BOOST_PP_NIL)))
или
BOOST_TTI_TEMPLATE(MyTemplate,(class)(int))
или
BOOST_TTI_TEMPLATE(MyTemplate,(class,int))
Единственное необходимое изменение будет в коде, который принимает второй параметр и преобразует его в конечную форму, используемую внутри. Это происходит в макро 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 для создания макросов, которые добавляют гибкость, когда макропрограммист чувствует, что он нуждается в ней для своей библиотеки.