...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
Examples of library use are always highly personal. Any given library employing macro programming can decide what macro facilities are needed based on the library itself and then decide if functionality in a macro library like VMD makes macro programming in that library easier. To that end the examples presented here are highly arbitrary and are just efforts to illustrate possible use of functionality of VMD features without worrying too much if those examples have any practical beneficial use in real programming situations. In these examples I have endeavored, therefore, to present macro programming "snippets" using VMD functionality rather than complete solutions to a given practical problem.
In C++ there is a 'switch' statement which we can emulate in macro programming using VMD. For the macro emulation we will have as parameters to our macro:
The macro looks like:
BOOST_VMD_SWITCH(value,calling_values,...)
We have to be careful not to parse the name of our macro to call in any way since this is a failing condition for BOOST_VMD_IS_EMPTY and subsequently for any parsing of input data we might want to do. Instead we will just extract the calling macro name and just call it, passing the calling values.
Our processing is:
Here is our code:
#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 */
The code is fairly involved but it is commented so that it can be understood. There are a few workarounds for a VC++ preprocessor problem, which I discovered, having to do with passing the name of a function-like macro in a tuple.
The BOOST_VMD_SWITCH macro can be used with either macros to call or with fixed values to return. When specifying macros to call the macro name is the second element of the corresponding value-macro tuple, or in the 'default' case it is just the macro name itself. When specifying fixed values to return the macro 'name' is BOOST_VMD_SWITCH_IDENTITY(fixed_value), whether as the second element of the corresponding value-macro tuple or as the macro 'name' of the 'default' case. In the variadic parameters the user can mix macro names and fixed values as he likes.
Some simple examples:
#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 /**/
We will use these simple macros in our calls to 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) )
Here our macro will return 'test1_7'.
Notice that 'cases' can be in any order.
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) )
Here are macro uses the default case and returns '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)) )
This shows how the matching case can be a fixed_value as the macro 'name'.
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)) )
This shows how the default value can be a fixed_value as the macro 'name'.
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) )
This shows that the 'value' and each 'case' matching values can be different data types just as long as the types are one which VMD can parse.
There is more that can be done with the BOOST_VMD_SWITCH code but as it is I believe it could be useful for programmers writing macro code. For instance there is no checking that more than one 'case' value is the same. We could generate a BOOST_VMD_ASSERT if that were the situation. There is no concept of falling through to the next 'case' as their is when 'break' is not used at the bottom of a particular C++ 'case' statement. Nonetheless the example gives the macro programmer an idea of what can be done using the BOOST_VMD_EQUAL macro in treating data types generically, using BOOST_VMD_IS_EMPTY to test for emptiness and using BOOST_VMD_IDENTITY to generate a fixed value when a macro call is made.
As a more practical example, just to show the possible use of VMD functionality in current Boost code, I will briefly illustrate a change that could be made to the TTI library when using VMD functionality.
The Boost TTI library, of which the current developer of VMD is also the developer, specifies a way to introspect an inner class template of a class. The introspection can occur for an inner class template of specific template parameters.
In the library a macro is used to generate the metafunction which allows the introspection to work. The macro used is called BOOST_TTI_TEMPLATE. The macro has both a variadic version and a non-variadic version.
In the non-variadic version the macro always takes two parameters for introspecting for specific template parameters. The first parameter is the name of the template and the second parameter is an array of the specific template parameters ( with or without the parameter names themselves ). So for a class template of the form:
template <class X,int Y> class MyTemplate { ... code };
the non-variadic macro would be:
BOOST_TTI_TEMPLATE(MyTemplate,(2,(class,int))) // uses array
I chose a Boost PP array rather than a Boost PP seq or a Boost PP list as I felt the notation for specifying the template parameters was closer with the array than with the others. Choosing a Boost PP tuple was not an option since for non-variadic macros there is no way to automatically know the tuple size, so an array was preferred.
For the variadic version variadic parameters are used so the notation would be:
BOOST_TTI_TEMPLATE(MyTemplate,class,int) // uses variadic parameters
since this is the most natural notation.
But for compatibility with the non-variadic version the end-user with variadic macro support could also choose the Boost PP array form above.
Using VMD the variadic version could support any of the other Boost PP composite types for the specific template parameters, even though I feel that the variadic parameters form is easiest to use. In this scenario a user could specify:
BOOST_TTI_TEMPLATE(MyTemplate,(class,(int,BOOST_PP_NIL))) // use a list
or
BOOST_TTI_TEMPLATE(MyTemplate,(class)(int)) // use a seq
or
BOOST_TTI_TEMPLATE(MyTemplate,(class,int)) // use a tuple
The only change needed would be in the code which takes the second parameter and converts it to the final form used internally ( a Boost PP array ). This occurs in the macro BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS in the <boost/tti/detail/dtemplate_params.hpp> file. The code has two situations, one for VC++8 or below and one for all other compilers. For our example we will concentrate just on the one for all other compilers. You do not need to know what the code does internally to complete the creation of the appropriate metafunction to follow this example. The macro code in question looks like this:
#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) ) ) \ ) \ /**/
In this code we are taking the name of the metafunction ( trait ), the name of the template ( name ), and our specific template parameters ( tpArray ) and passing the information in the form of a Boost PP array to another macro, which will eventually create the metafunction which the end-user uses to test if such a class template exists within some enclosing class. Even if tpArray were a list, seq, or tuple we still want to pass the information internally to BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE in the form you can see above, which is a Boost PP array. We don't need or want to change that internal representation.
The current code, used by both the non-variadic and variadic version of the BOOST_TTI_TEMPLATE template, assumes the 'tpArray' parameter is a Boost PP array. But if it could be a tuple, seq, or list in the variadic version the code could become, with the appropriate Boost PP and VMD header files:
#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
This of course gets more elaborate, but could be shortened considerably if we chose to use BOOST_VMD_GET_TYPE and the invented BOOST_VMD_SWITCH of our first example. We will assume in this second version of the code above that our BOOST_VMD_SWITCH macro has been #included from somewhere.
#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
This is shorter and easier to understand. The '(1)' passed as the calling values to BOOST_VMD_SWITCH could just as well be '()' but VC8 has trouble with empty parentheses so I avoid it here.
In the case of the TTI, is such a change worth it to give more flexibility to the end-user ? In reality, because the variadic version of passing the specific template parameters as variadic data is syntactically easier to use than any of the Boost PP composite forms, I am actually happy enough with that use not to pursue the sort of functionality I presented in this example. But the example nonetheless shows the power of the VMD functionality for creating macros which add flexibility when the macro programmer feels he needs it for his library.