361 lines
14 KiB
Plaintext
361 lines
14 KiB
Plaintext
[/
|
|
(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] |