1[/ 2 (C) Copyright Edward Diener 2011-2015 3 Distributed under the Boost Software License, Version 1.0. 4 (See accompanying file LICENSE_1_0.txt or copy at 5 http://www.boost.org/LICENSE_1_0.txt). 6] 7 8[section:vmd_examples Examples using VMD functionality] 9 10Examples of library use are always highly personal. Any given library 11employing macro programming can decide what macro facilities are needed 12based on the library itself and then decide if functionality in a macro 13library like VMD makes macro programming in that library easier. To that end 14the examples presented here are highly arbitrary and are just efforts to 15illustrate possible use of functionality of VMD features without worrying 16too much if those examples have any practical beneficial use in real 17programming situations. In these examples I have endeavored, therefore, 18to present macro programming "snippets" using VMD functionality rather than 19complete solutions to a given practical problem. 20 21[heading Switch macro] 22 23[import ../test/test_doc_example_switch.hpp] 24[import ../test/test_doc_example_switch.cxx] 25 26In C++ there is a 'switch' statement which we can emulate in macro programming 27using VMD. For the macro emulation we will have as parameters to our macro: 28 29# A value, which can be any data type VMD can parse. 30# A tuple of calling values. These will be used when calling the matching macro. 31# Variadic parameters, each of which are tuples. 32Each tuple consists of two elements, the name of 33a value to match and the name of a macro to call. 34For the 'default' case the tuple is a single element 35which is the name of a macro to call. These are our 36equivalents to the C++ switch 'case' statements. 37 38The macro looks like: 39 40 BOOST_VMD_SWITCH(value,calling_values,...) 41 42We have to be careful not to parse the name of our macro to call 43in any way since this is a failing condition for BOOST_VMD_IS_EMPTY 44and subsequently for any parsing of input data we might want to do. 45Instead we will just extract the calling macro name and just call 46it, passing the calling values. 47 48Our processing is: 49 50# Convert our variadic parameters to a tuple since access to tuple 51elements is easier. 52# Use a BOOST_PP_WHILE loop to find the matching value and extract 53the calling macro from it. We will use BOOST_VMD_EQUAL to find the 54matching value. 55# Call the calling macro with the calling values when we return from 56our BOOST_PP_WHILE loop. 57 58Here is our code: 59 60[example_switch] 61 62The code is fairly involved but it is commented so that it can be 63understood. There are a few workarounds for a VC++ preprocessor 64problem, which I discovered, having to do with passing the name of a function-like 65macro in a tuple. 66 67The BOOST_VMD_SWITCH macro can be used with either macros to call 68or with fixed values to return. When specifying macros to call the 69macro name is the second element of the corresponding value-macro 70tuple, or in the 'default' case it is just the macro name itself. 71When specifying fixed values to return the macro 'name' is 72BOOST_VMD_SWITCH_IDENTITY(fixed_value), whether as the second 73element of the corresponding value-macro tuple or as the macro 74'name' of the 'default' case. In the variadic parameters the 75user can mix macro names and fixed values as he likes. 76 77Some simple examples: 78 79[example_switch_defines] 80 81We will use these simple macros in our calls to BOOST_VMD_SWITCH. 82 83[example_switch_defines_t1] 84 85Here our macro will return 'test1_7'. 86 87Notice that 'cases' can be in any order. 88 89[example_switch_defines_t4] 90 91Here are macro uses the default case and returns 'test_default_7'. 92 93[example_switch_defines_t5] 94 95This shows how the matching case can be a fixed_value as the macro 'name'. 96 97[example_switch_defines_t6] 98 99This shows how the default value can be a fixed_value as the macro 'name'. 100 101[example_switch_defines_t7] 102 103This shows that the 'value' and each 'case' matching values can be different 104data types just as long as the types are one which VMD can parse. 105 106There is more that can be done with the BOOST_VMD_SWITCH code but as it is 107I believe it could be useful for programmers writing macro code. For instance 108there is no checking that more than one 'case' value is the same. We could 109generate a BOOST_VMD_ASSERT if that were the situation. There is no concept 110of falling through to the next 'case' as their is when 'break' is not used 111at the bottom of a particular C++ 'case' statement. Nonetheless the example 112gives the macro programmer an idea of what can be done using the BOOST_VMD_EQUAL 113macro in treating data types generically, using BOOST_VMD_IS_EMPTY to test for 114emptiness and using BOOST_VMD_IDENTITY to generate a fixed value when a macro call 115is made. 116 117[heading TTI inner template] 118 119As a more practical example, just to show the possible use of VMD functionality 120in current Boost code, I will briefly illustrate a change that could be made to 121the TTI library when using VMD functionality. 122 123The Boost TTI library, of which the current developer of VMD is also the developer, 124specifies a way to introspect an inner class template of a class. The introspection 125can occur for an inner class template of specific template parameters. 126 127In the library a macro is used to generate the metafunction which allows the introspection to work. 128The macro used is called BOOST_TTI_TEMPLATE. The macro has both a variadic version and 129a non-variadic version. 130 131In the non-variadic version the macro always takes two parameters for introspecting 132for specific template parameters. The first parameter is the name of the template 133and the second parameter is an array of the specific template parameters ( with or without 134the parameter names themselves ). So for a class template of the form: 135 136 template <class X,int Y> class MyTemplate { ... code }; 137 138the non-variadic macro would be: 139 140 BOOST_TTI_TEMPLATE(MyTemplate,(2,(class,int))) // uses array 141 142I chose a Boost PP array rather than a Boost PP seq or a Boost PP list as I felt the notation 143for specifying the template parameters was closer with the array than with the others. 144Choosing a Boost PP tuple was not an option since for non-variadic macros there is no 145way to automatically know the tuple size, so an array was preferred. 146 147For the variadic version variadic parameters are used so the notation would be: 148 149 BOOST_TTI_TEMPLATE(MyTemplate,class,int) // uses variadic parameters 150 151since this is the most natural notation. 152 153But for compatibility with the non-variadic version the end-user 154with variadic macro support could also choose the Boost PP array form above. 155 156Using VMD the variadic version could support any of the other Boost PP 157composite types for the specific template parameters, even though I feel 158that the variadic parameters form is easiest to use. In this scenario 159a user could specify: 160 161 BOOST_TTI_TEMPLATE(MyTemplate,(class,(int,BOOST_PP_NIL))) // use a list 162 163or 164 165 BOOST_TTI_TEMPLATE(MyTemplate,(class)(int)) // use a seq 166 167or 168 169 BOOST_TTI_TEMPLATE(MyTemplate,(class,int)) // use a tuple 170 171The only change needed would be in the code which takes the second parameter 172and converts it to the final form used internally ( a Boost PP array ). 173This occurs in the macro BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS in 174the <boost/tti/detail/dtemplate_params.hpp> file. The code has two situations, one 175for VC++8 or below and one for all other compilers. For our example we will concentrate 176just on the one for all other compilers. You do not need to know what the code does 177internally to complete the creation of the appropriate metafunction to follow this 178example. The macro code in question looks like this: 179 180 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \ 181 BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \ 182 ( \ 183 ( BOOST_PP_ADD(BOOST_PP_ARRAY_SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ARRAY_ENUM(tpArray) ) ) \ 184 ) \ 185 /**/ 186 187In this code we are taking the name of the metafunction ( trait ), the name of the 188template ( name ), and our specific template parameters ( tpArray ) and passing the 189information in the form of a Boost PP array to another macro, which will eventually 190create the metafunction which the end-user uses to test if such a class template 191exists within some enclosing class. Even if tpArray were a list, seq, or tuple we 192still want to pass the information internally to BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE 193in the form you can see above, which is a Boost PP array. We don't need or want to 194change that internal representation. 195 196The current code, used by both the non-variadic and variadic version of the 197BOOST_TTI_TEMPLATE template, assumes the 'tpArray' parameter is a Boost PP array. 198But if it could be a tuple, seq, or list in the variadic version the code could become, 199with the appropriate Boost PP and VMD header files: 200 201 #include <boost/preprocessor/arithmetic/add.hpp> 202 #include <boost/preprocessor/array/enum.hpp> 203 #include <boost/preprocessor/array/size.hpp> 204 #include <boost/preprocessor/control/expr_iif.hpp> 205 #include <boost/preprocessor/control/iif.hpp> 206 #include <boost/preprocessor/list/enum.hpp> 207 #include <boost/preprocessor/list/size.hpp> 208 #include <boost/preprocessor/seq/enum.hpp> 209 #include <boost/preprocessor/seq/size.hpp> 210 #include <boost/preprocessor/tuple/enum.hpp> 211 #include <boost/preprocessor/tuple/size.hpp> 212 #include <boost/vmd/identity.hpp> 213 #include <boost/vmd/is_array.hpp> 214 #include <boost/vmd/is_list.hpp> 215 #include <boost/vmd/is_seq.hpp> 216 #include <boost/vmd/is_tuple.hpp> 217 218 #if BOOST_PP_VARIADICS 219 220 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \ 221 BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \ 222 ( \ 223 BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT \ 224 ( \ 225 trait,name,tpArray, \ 226 BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE(tpArray) \ 227 ) \ 228 ) \ 229 /**/ 230 231 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE(tpArray) \ 232 BOOST_VMD_IDENTITY_RESULT \ 233 ( \ 234 BOOST_PP_IIF \ 235 ( \ 236 BOOST_VMD_IS_ARRAY(tpArray), \ 237 BOOST_VMD_IDENTITY(ARRAY), \ 238 BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_LIST \ 239 ) \ 240 (tpArray) \ 241 ) \ 242 /**/ 243 244 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_LIST(tpArray) \ 245 BOOST_VMD_IDENTITY_RESULT \ 246 ( \ 247 BOOST_PP_IIF \ 248 ( \ 249 BOOST_VMD_IS_LIST(tpArray), \ 250 BOOST_VMD_IDENTITY(LIST), \ 251 BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_SEQ \ 252 ) \ 253 (tpArray) \ 254 ) \ 255 /**/ 256 257 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_SEQ(tpArray) \ 258 BOOST_VMD_IDENTITY_RESULT \ 259 ( \ 260 BOOST_PP_IIF \ 261 ( \ 262 BOOST_VMD_IS_SEQ(tpArray), \ 263 BOOST_VMD_IDENTITY(SEQ), \ 264 BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_TUPLE \ 265 ) \ 266 (tpArray) \ 267 ) \ 268 /**/ 269 270 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_TUPLE(tpArray) \ 271 BOOST_VMD_IDENTITY_RESULT \ 272 ( \ 273 BOOST_PP_EXPR_IIF \ 274 ( \ 275 BOOST_VMD_IS_TUPLE(tpArray), \ 276 BOOST_VMD_IDENTITY(TUPLE) \ 277 ) \ 278 ) \ 279 /**/ 280 281 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT(trait,name,tpArray,name) \ 282 ( BOOST_PP_ADD(BOOST_PP_ ## name ## _SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ ## name ## _ENUM(tpArray) ) ) \ 283 /**/ 284 285 #else 286 287 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \ 288 BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \ 289 ( \ 290 ( BOOST_PP_ADD(BOOST_PP_ARRAY_SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ARRAY_ENUM(tpArray) ) ) \ 291 ) \ 292 /**/ 293 294 #endif 295 296This of course gets more elaborate, but could be shortened considerably if we chose to 297use BOOST_VMD_GET_TYPE and the invented BOOST_VMD_SWITCH of our first example. We will 298assume in this second version of the code above that our BOOST_VMD_SWITCH macro has 299been #included from somewhere. 300 301 #include <boost/preprocessor/arithmetic/add.hpp> 302 #include <boost/preprocessor/array/enum.hpp> 303 #include <boost/preprocessor/array/size.hpp> 304 #include <boost/preprocessor/list/enum.hpp> 305 #include <boost/preprocessor/list/size.hpp> 306 #include <boost/preprocessor/seq/enum.hpp> 307 #include <boost/preprocessor/seq/size.hpp> 308 #include <boost/preprocessor/tuple/enum.hpp> 309 #include <boost/preprocessor/tuple/size.hpp> 310 #include <boost/vmd/get_type.hpp> 311 312 #if BOOST_PP_VARIADICS 313 314 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \ 315 BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \ 316 ( \ 317 BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT \ 318 ( \ 319 trait,name,tpArray, \ 320 BOOST_VMD_SWITCH \ 321 ( \ 322 BOOST_VMD_GET_TYPE(tpArray), \ 323 (1), \ 324 (BOOST_VMD_TYPE_ARRAY,BOOST_VMD_SWITCH_IDENTITY(ARRAY)), \ 325 (BOOST_VMD_TYPE_LIST,BOOST_VMD_SWITCH_IDENTITY(LIST)), \ 326 (BOOST_VMD_TYPE_SEQ,BOOST_VMD_SWITCH_IDENTITY(SEQ)), \ 327 (BOOST_VMD_TYPE_TUPLE,BOOST_VMD_SWITCH_IDENTITY(TUPLE)) \ 328 ) \ 329 ) \ 330 ) \ 331 /**/ 332 333 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS_TYPE_CONCAT(trait,name,tpArray,name) \ 334 ( BOOST_PP_ADD(BOOST_PP_ ## name ## _SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ ## name ## _ENUM(tpArray) ) ) \ 335 /**/ 336 337 #else 338 339 #define BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS(trait,name,tpArray) \ 340 BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE \ 341 ( \ 342 ( BOOST_PP_ADD(BOOST_PP_ARRAY_SIZE(tpArray),4), ( trait, name, 1, false, BOOST_PP_ARRAY_ENUM(tpArray) ) ) \ 343 ) \ 344 /**/ 345 346 #endif 347 348This is shorter and easier to understand. The '(1)' passed as the calling 349values to BOOST_VMD_SWITCH could just as well be '()' but VC8 has trouble 350with empty parentheses so I avoid it here. 351 352In the case of the TTI, is such a change worth it to give more flexibility 353to the end-user ? In reality, because the variadic version of passing the 354specific template parameters as variadic data is syntactically easier to use than 355any of the Boost PP composite forms, I am actually happy enough with that use 356not to pursue the sort of functionality I presented in this example. But the 357example nonetheless shows the power of the VMD functionality for creating 358macros which add flexibility when the macro programmer feels he needs it 359for his library. 360 361[endsect]