vmd_examples.qbk 14 KB

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