]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | ||
10 | Examples of library use are always highly personal. Any given library | |
11 | employing macro programming can decide what macro facilities are needed | |
12 | based on the library itself and then decide if functionality in a macro | |
13 | library like VMD makes macro programming in that library easier. To that end | |
14 | the examples presented here are highly arbitrary and are just efforts to | |
15 | illustrate possible use of functionality of VMD features without worrying | |
16 | too much if those examples have any practical beneficial use in real | |
17 | programming situations. In these examples I have endeavored, therefore, | |
18 | to present macro programming "snippets" using VMD functionality rather than | |
19 | complete 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 | ||
26 | In C++ there is a 'switch' statement which we can emulate in macro programming | |
27 | using 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. | |
32 | Each tuple consists of two elements, the name of | |
33 | a value to match and the name of a macro to call. | |
34 | For the 'default' case the tuple is a single element | |
35 | which is the name of a macro to call. These are our | |
36 | equivalents to the C++ switch 'case' statements. | |
37 | ||
38 | The macro looks like: | |
39 | ||
40 | BOOST_VMD_SWITCH(value,calling_values,...) | |
41 | ||
42 | We have to be careful not to parse the name of our macro to call | |
43 | in any way since this is a failing condition for BOOST_VMD_IS_EMPTY | |
44 | and subsequently for any parsing of input data we might want to do. | |
45 | Instead we will just extract the calling macro name and just call | |
46 | it, passing the calling values. | |
47 | ||
48 | Our processing is: | |
49 | ||
50 | # Convert our variadic parameters to a tuple since access to tuple | |
51 | elements is easier. | |
52 | # Use a BOOST_PP_WHILE loop to find the matching value and extract | |
53 | the calling macro from it. We will use BOOST_VMD_EQUAL to find the | |
54 | matching value. | |
55 | # Call the calling macro with the calling values when we return from | |
56 | our BOOST_PP_WHILE loop. | |
57 | ||
58 | Here is our code: | |
59 | ||
60 | [example_switch] | |
61 | ||
62 | The code is fairly involved but it is commented so that it can be | |
63 | understood. There are a few workarounds for a VC++ preprocessor | |
64 | problem, which I discovered, having to do with passing the name of a function-like | |
65 | macro in a tuple. | |
66 | ||
67 | The BOOST_VMD_SWITCH macro can be used with either macros to call | |
68 | or with fixed values to return. When specifying macros to call the | |
69 | macro name is the second element of the corresponding value-macro | |
70 | tuple, or in the 'default' case it is just the macro name itself. | |
71 | When specifying fixed values to return the macro 'name' is | |
72 | BOOST_VMD_SWITCH_IDENTITY(fixed_value), whether as the second | |
73 | element of the corresponding value-macro tuple or as the macro | |
74 | 'name' of the 'default' case. In the variadic parameters the | |
75 | user can mix macro names and fixed values as he likes. | |
76 | ||
77 | Some simple examples: | |
78 | ||
79 | [example_switch_defines] | |
80 | ||
81 | We will use these simple macros in our calls to BOOST_VMD_SWITCH. | |
82 | ||
83 | [example_switch_defines_t1] | |
84 | ||
85 | Here our macro will return 'test1_7'. | |
86 | ||
87 | Notice that 'cases' can be in any order. | |
88 | ||
89 | [example_switch_defines_t4] | |
90 | ||
91 | Here are macro uses the default case and returns 'test_default_7'. | |
92 | ||
93 | [example_switch_defines_t5] | |
94 | ||
95 | This shows how the matching case can be a fixed_value as the macro 'name'. | |
96 | ||
97 | [example_switch_defines_t6] | |
98 | ||
99 | This shows how the default value can be a fixed_value as the macro 'name'. | |
100 | ||
101 | [example_switch_defines_t7] | |
102 | ||
103 | This shows that the 'value' and each 'case' matching values can be different | |
104 | data types just as long as the types are one which VMD can parse. | |
105 | ||
106 | There is more that can be done with the BOOST_VMD_SWITCH code but as it is | |
107 | I believe it could be useful for programmers writing macro code. For instance | |
108 | there is no checking that more than one 'case' value is the same. We could | |
109 | generate a BOOST_VMD_ASSERT if that were the situation. There is no concept | |
110 | of falling through to the next 'case' as their is when 'break' is not used | |
111 | at the bottom of a particular C++ 'case' statement. Nonetheless the example | |
112 | gives the macro programmer an idea of what can be done using the BOOST_VMD_EQUAL | |
113 | macro in treating data types generically, using BOOST_VMD_IS_EMPTY to test for | |
114 | emptiness and using BOOST_VMD_IDENTITY to generate a fixed value when a macro call | |
115 | is made. | |
116 | ||
117 | [heading TTI inner template] | |
118 | ||
119 | As a more practical example, just to show the possible use of VMD functionality | |
120 | in current Boost code, I will briefly illustrate a change that could be made to | |
121 | the TTI library when using VMD functionality. | |
122 | ||
123 | The Boost TTI library, of which the current developer of VMD is also the developer, | |
124 | specifies a way to introspect an inner class template of a class. The introspection | |
125 | can occur for an inner class template of specific template parameters. | |
126 | ||
127 | In the library a macro is used to generate the metafunction which allows the introspection to work. | |
128 | The macro used is called BOOST_TTI_TEMPLATE. The macro has both a variadic version and | |
129 | a non-variadic version. | |
130 | ||
131 | In the non-variadic version the macro always takes two parameters for introspecting | |
132 | for specific template parameters. The first parameter is the name of the template | |
133 | and the second parameter is an array of the specific template parameters ( with or without | |
134 | the parameter names themselves ). So for a class template of the form: | |
135 | ||
136 | template <class X,int Y> class MyTemplate { ... code }; | |
137 | ||
138 | the non-variadic macro would be: | |
139 | ||
140 | BOOST_TTI_TEMPLATE(MyTemplate,(2,(class,int))) // uses array | |
141 | ||
142 | I chose a Boost PP array rather than a Boost PP seq or a Boost PP list as I felt the notation | |
143 | for specifying the template parameters was closer with the array than with the others. | |
144 | Choosing a Boost PP tuple was not an option since for non-variadic macros there is no | |
145 | way to automatically know the tuple size, so an array was preferred. | |
146 | ||
147 | For the variadic version variadic parameters are used so the notation would be: | |
148 | ||
149 | BOOST_TTI_TEMPLATE(MyTemplate,class,int) // uses variadic parameters | |
150 | ||
151 | since this is the most natural notation. | |
152 | ||
153 | But for compatibility with the non-variadic version the end-user | |
154 | with variadic macro support could also choose the Boost PP array form above. | |
155 | ||
156 | Using VMD the variadic version could support any of the other Boost PP | |
157 | composite types for the specific template parameters, even though I feel | |
158 | that the variadic parameters form is easiest to use. In this scenario | |
159 | a user could specify: | |
160 | ||
161 | BOOST_TTI_TEMPLATE(MyTemplate,(class,(int,BOOST_PP_NIL))) // use a list | |
162 | ||
163 | or | |
164 | ||
165 | BOOST_TTI_TEMPLATE(MyTemplate,(class)(int)) // use a seq | |
166 | ||
167 | or | |
168 | ||
169 | BOOST_TTI_TEMPLATE(MyTemplate,(class,int)) // use a tuple | |
170 | ||
171 | The only change needed would be in the code which takes the second parameter | |
172 | and converts it to the final form used internally ( a Boost PP array ). | |
173 | This occurs in the macro BOOST_TTI_DETAIL_TRAIT_CALL_HAS_TEMPLATE_CHECK_PARAMS in | |
174 | the <boost/tti/detail/dtemplate_params.hpp> file. The code has two situations, one | |
175 | for VC++8 or below and one for all other compilers. For our example we will concentrate | |
176 | just on the one for all other compilers. You do not need to know what the code does | |
177 | internally to complete the creation of the appropriate metafunction to follow this | |
178 | example. 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 | ||
187 | In this code we are taking the name of the metafunction ( trait ), the name of the | |
188 | template ( name ), and our specific template parameters ( tpArray ) and passing the | |
189 | information in the form of a Boost PP array to another macro, which will eventually | |
190 | create the metafunction which the end-user uses to test if such a class template | |
191 | exists within some enclosing class. Even if tpArray were a list, seq, or tuple we | |
192 | still want to pass the information internally to BOOST_TTI_DETAIL_HAS_MEMBER_WITH_TEMPLATE_SFINAE | |
193 | in the form you can see above, which is a Boost PP array. We don't need or want to | |
194 | change that internal representation. | |
195 | ||
196 | The current code, used by both the non-variadic and variadic version of the | |
197 | BOOST_TTI_TEMPLATE template, assumes the 'tpArray' parameter is a Boost PP array. | |
198 | But if it could be a tuple, seq, or list in the variadic version the code could become, | |
199 | with 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 | ||
296 | This of course gets more elaborate, but could be shortened considerably if we chose to | |
297 | use BOOST_VMD_GET_TYPE and the invented BOOST_VMD_SWITCH of our first example. We will | |
298 | assume in this second version of the code above that our BOOST_VMD_SWITCH macro has | |
299 | been #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 | ||
348 | This is shorter and easier to understand. The '(1)' passed as the calling | |
349 | values to BOOST_VMD_SWITCH could just as well be '()' but VC8 has trouble | |
350 | with empty parentheses so I avoid it here. | |
351 | ||
352 | In the case of the TTI, is such a change worth it to give more flexibility | |
353 | to the end-user ? In reality, because the variadic version of passing the | |
354 | specific template parameters as variadic data is syntactically easier to use than | |
355 | any of the Boost PP composite forms, I am actually happy enough with that use | |
356 | not to pursue the sort of functionality I presented in this example. But the | |
357 | example nonetheless shows the power of the VMD functionality for creating | |
358 | macros which add flexibility when the macro programmer feels he needs it | |
359 | for his library. | |
360 | ||
361 | [endsect] |