123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- [/
- (C) Copyright Edward Diener 2011-2015
- Distributed under the Boost Software License, Version 1.0.
- (See accompanying file LICENSE_1_0.txt or copy at
- http://www.boost.org/LICENSE_1_0.txt).
- ]
- [section:vmd_examples Examples using VMD functionality]
- 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.
- [heading Switch macro]
- [import ../test/test_doc_example_switch.hpp]
- [import ../test/test_doc_example_switch.cxx]
- 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:
- # A value, which can be any data type VMD can parse.
- # A tuple of calling values. These will be used when calling the matching macro.
- # Variadic parameters, each of which are tuples.
- Each tuple consists of two elements, the name of
- a value to match and the name of a macro to call.
- For the 'default' case the tuple is a single element
- which is the name of a macro to call. These are our
- equivalents to the C++ switch 'case' statements.
- 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:
- # Convert our variadic parameters to a tuple since access to tuple
- elements is easier.
- # Use a BOOST_PP_WHILE loop to find the matching value and extract
- the calling macro from it. We will use BOOST_VMD_EQUAL to find the
- matching value.
- # Call the calling macro with the calling values when we return from
- our BOOST_PP_WHILE loop.
- Here is our code:
- [example_switch]
- 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:
- [example_switch_defines]
- We will use these simple macros in our calls to BOOST_VMD_SWITCH.
- [example_switch_defines_t1]
- Here our macro will return 'test1_7'.
- Notice that 'cases' can be in any order.
- [example_switch_defines_t4]
- Here are macro uses the default case and returns 'test_default_7'.
- [example_switch_defines_t5]
- This shows how the matching case can be a fixed_value as the macro 'name'.
- [example_switch_defines_t6]
- This shows how the default value can be a fixed_value as the macro 'name'.
- [example_switch_defines_t7]
- 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.
- [heading TTI inner template]
- 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.
-
- [endsect]
|