1 | <html> |
---|
2 | <head> |
---|
3 | <title>techniques.html</title> |
---|
4 | <link rel="stylesheet" type="text/css" href="../styles.css"> |
---|
5 | <style> |
---|
6 | u { font-weight: normal; text-decoration: none; } |
---|
7 | </style> |
---|
8 | </head> |
---|
9 | <body> |
---|
10 | <h4>Techniques</h4> |
---|
11 | <div> |
---|
12 | The preprocessor metaprogramming techniques are presented in example format. |
---|
13 | </div> |
---|
14 | |
---|
15 | <h4>Example<u> - Use a local macro to avoid small scale repetition.</u></h4> |
---|
16 | <div class="code"><pre> |
---|
17 | #define BOOST_PP_DEF(op) /* ..................................... */ \ |
---|
18 | template<class T, int n> \ |
---|
19 | vec<T, n> operator op ## =(vec<T, n> lhs, const vec<T, n>& rhs) { \ |
---|
20 | for (int i = 0; i < n; ++i) { \ |
---|
21 | lhs(i) op ## = rhs(i); \ |
---|
22 | } \ |
---|
23 | } \ |
---|
24 | /**/ |
---|
25 | |
---|
26 | BOOST_PP_DEF(+) |
---|
27 | BOOST_PP_DEF(-) |
---|
28 | BOOST_PP_DEF(*) |
---|
29 | BOOST_PP_DEF(/) |
---|
30 | |
---|
31 | #undef BOOST_PP_DEF |
---|
32 | </pre></div> |
---|
33 | <div> |
---|
34 | <b>Tip:</b> It is usually okay to use a standard macro name like <code>BOOST_PP_DEF</code> for this kind of code |
---|
35 | because the macro is both defined and undefined in the immediate site of its use. |
---|
36 | </div> |
---|
37 | <div> |
---|
38 | <b>Tip:</b> It is easier to verify proper use of the line continuation operator when they are aligned. |
---|
39 | </div> |
---|
40 | <div> |
---|
41 | <b>Notes:</b> You can extend this example by defining more and different kinds of operators. |
---|
42 | Before doing so, consider using the <i>algebraic categories</i> technique introduced in <a href="../bibliography.html#barton">[Barton]</a> |
---|
43 | or a <i>layered architecture</i> (see for instance <a href="../bibliography.html#czarnecki">[Czarnecki]</a>). |
---|
44 | However, at some point you must type the operator tokens <code>*</code>, <code>/</code>, <code>+</code>, <code>-</code>, etc., |
---|
45 | because it is impossible to generate them using templates. |
---|
46 | The resulting <i>categorical repetition</i> of tokens can be eliminated by using preprocessor metaprogramming. |
---|
47 | </div> |
---|
48 | |
---|
49 | <h4>Example<u> - Use BOOST_PP_EMPTY as an unused parameter in local macro instantiations.</u></h4> |
---|
50 | <div class="code"><pre> |
---|
51 | #define BOOST_PP_DEF(cv) /* ... */ \ |
---|
52 | template<class base> \ |
---|
53 | cv() typename implement_subscript_using_begin_subscript<base>::value_type& \ |
---|
54 | implement_subscript_using_begin_subscript<base>::operator[](index_type i) cv() { \ |
---|
55 | return base::begin()[i]; \ |
---|
56 | } \ |
---|
57 | /**/ |
---|
58 | |
---|
59 | BOOST_PP_DEF(BOOST_PP_EMPTY) |
---|
60 | BOOST_PP_DEF(BOOST_PP_IDENTITY(const)) |
---|
61 | </pre></div> |
---|
62 | <div> |
---|
63 | <b>How:</b> BOOST_PP_EMPTY() expands to nothing and can be used as an unused parameter. |
---|
64 | </div> |
---|
65 | <div> |
---|
66 | <b>Note:</b> BOOST_PP_EMPTY with the () never gets expanded. |
---|
67 | The () is necessary to invoke a function-like macro. |
---|
68 | </div> |
---|
69 | <div> |
---|
70 | <b>Caveat:</b> You cannot safely use concatenation while using BOOST_PP_EMPTY(). |
---|
71 | </div> |
---|
72 | <div> |
---|
73 | <b>Tip:</b> Occasionally, one or two lines are considerably longer than the rest. |
---|
74 | It can often save some work to <i>not</i> align all the line continuation operators without making the code too unreadable. |
---|
75 | </div> |
---|
76 | <div> |
---|
77 | <b>Tip:</b> Use syntax highlighting on preprocessor metaprogramming macro identifiers such as: |
---|
78 | <ul> |
---|
79 | <li>BOOST_PP_DEF</li> |
---|
80 | <li>BOOST_PP_EMPTY</li> |
---|
81 | <li>BOOST_PP_REPEAT</li> |
---|
82 | <li>...</li> |
---|
83 | </ul> |
---|
84 | It can greatly improve readability. |
---|
85 | </div> |
---|
86 | |
---|
87 | <h4>Example<u> - Use BOOST_PP_CAT instead of ## when necessary.</u></h4> |
---|
88 | <div class="code"><pre> |
---|
89 | #define STATIC_ASSERT(expr) \ |
---|
90 | enum { BOOST_PP_CAT(static_check_, __LINE__) = (expr) ? 1 : -1 }; \ |
---|
91 | typedef char \ |
---|
92 | BOOST_PP_CAT(static_assert_, __LINE__)[BOOST_PP_CAT(static_check_, __LINE__)] \ |
---|
93 | /**/ |
---|
94 | |
---|
95 | // ... |
---|
96 | |
---|
97 | STATIC_ASSERT(sizeof(int) <= sizeof(long)); |
---|
98 | </pre></div> |
---|
99 | <div> |
---|
100 | <b>Why:</b> Macro expansion proceeds recursively in "layers." |
---|
101 | Token pasting prevents the preprocessor from performing macro expansion, |
---|
102 | therefore it is often necessary to delay token concatenation. |
---|
103 | </div> |
---|
104 | |
---|
105 | <h4>Example<u> - Use BOOST_PP_STRINGIZE instead of # whenever necessary.</u></h4> |
---|
106 | <div class="code"><pre> |
---|
107 | #define NOTE(str) \ |
---|
108 | message(__FILE__ "(" BOOST_PP_STRINGIZE(__LINE__) ") : " str) \ |
---|
109 | /**/ |
---|
110 | |
---|
111 | // ... |
---|
112 | |
---|
113 | #pragma NOTE("TBD!") |
---|
114 | </pre></div> |
---|
115 | <div> |
---|
116 | <b>Why:</b> Macro expansion proceeds recursively in "layers." |
---|
117 | Stringization prevents the preprocessor from performing macro expansion, therefore it is often necessary to delay stringization. |
---|
118 | </div> |
---|
119 | |
---|
120 | <h4>Example<u> - Use BOOST_PP_ENUM_PARAMS (and its variants) or BOOST_PP_REPEAT and BOOST_PP_COMMA_IF to avoid <i>O</i>(<i>n</i>) repetition on lists in general.</u></h4> |
---|
121 | <div class="code"><pre> |
---|
122 | struct make_type_list_end; |
---|
123 | |
---|
124 | template< |
---|
125 | BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT( |
---|
126 | MAKE_TYPE_LIST_MAX_LENGTH, |
---|
127 | class T, |
---|
128 | make_type_list_end |
---|
129 | ) |
---|
130 | > |
---|
131 | struct make_type_list { |
---|
132 | private: |
---|
133 | enum { end = is_same<T0, make_type_list_end>::value }; |
---|
134 | public: |
---|
135 | typedef typename type_if< |
---|
136 | end, type_cons_empty, |
---|
137 | type_cons< |
---|
138 | T0, |
---|
139 | typename type_inner_if< |
---|
140 | end, type_identity<end>, |
---|
141 | make_type_list< |
---|
142 | BOOST_PP_ENUM_SHIFTED_PARAMS( |
---|
143 | MAKE_TYPE_LIST_MAX_LENGTH, |
---|
144 | T |
---|
145 | ) |
---|
146 | > |
---|
147 | >::type |
---|
148 | > |
---|
149 | >::type type; |
---|
150 | }; |
---|
151 | </pre></div> |
---|
152 | <div> |
---|
153 | <b>How:</b> BOOST_PP_REPEAT uses simulated recursion (pseudo code): |
---|
154 | </div> |
---|
155 | <div><pre> |
---|
156 | #define BOOST_PP_REPEAT(n, m, p) BOOST_PP_REPEAT ## n(m, p) |
---|
157 | |
---|
158 | #define BOOST_PP_REPEAT0(m, p) |
---|
159 | #define BOOST_PP_REPEAT1(m, p) m(0, p) |
---|
160 | #define BOOST_PP_REPEAT2(m, p) m(0, p) m(1, p) |
---|
161 | #define BOOST_PP_REPEAT3(m, p) BOOST_PP_REPEAT2(m, p) m(2, p) |
---|
162 | #define BOOST_PP_REPEAT4(m, p) BOOST_PP_REPEAT3(m, p) m(3, p) |
---|
163 | // ... |
---|
164 | </pre></div> |
---|
165 | <div> |
---|
166 | <i>Note: This is no longer how BOOST_PP_REPEAT is implemented, so the code above is illustrative only! </i> |
---|
167 | </div> |
---|
168 | <div> |
---|
169 | BOOST_PP_ENUM_PARAMS and its variations use BOOST_PP_REPEAT. |
---|
170 | BOOST_PP_COMMA_IF(I) expands to a comma if I != 0. |
---|
171 | BOOST_PP_INC(I) essentially expands to "I+1," and BOOST_PP_DEC(I) essentially expands to "I-1.". |
---|
172 | </div> |
---|
173 | |
---|
174 | <h4>Example<u> - Use a <i>conditional macro definition</i> to enable user configuration of code repetition based on need rather than some "reasonable" upper limit.</u></h4> |
---|
175 | <div class="code"><pre> |
---|
176 | #ifndef MAKE_TYPE_LIST_MAX_LENGTH |
---|
177 | #define MAKE_TYPE_LIST_MAX_LENGTH 8 |
---|
178 | #endif |
---|
179 | </pre></div> |
---|
180 | <div> |
---|
181 | Now the user can configure the <code>make_type_list</code> primitive without modifying library code. |
---|
182 | </div> |
---|
183 | |
---|
184 | <h4>Example<u> - Use BOOST_PP_REPEAT and a <i>token look-up function</i> to eliminate categorical repetition.</u></h4> |
---|
185 | <div class="code"><pre> |
---|
186 | // CAVEAT: My compiler is not standard on arithmetic types. |
---|
187 | #define ARITHMETIC_TYPE(I) ARITHMETIC_TYPE ## I |
---|
188 | |
---|
189 | #define ARITHMETIC_TYPE0 bool |
---|
190 | #define ARITHMETIC_TYPE1 char |
---|
191 | #define ARITHMETIC_TYPE2 signed char |
---|
192 | #define ARITHMETIC_TYPE3 unsigned char |
---|
193 | #define ARITHMETIC_TYPE4 short |
---|
194 | #define ARITHMETIC_TYPE5 unsigned short |
---|
195 | #define ARITHMETIC_TYPE6 int |
---|
196 | #define ARITHMETIC_TYPE7 unsigned int |
---|
197 | #define ARITHMETIC_TYPE8 long |
---|
198 | #define ARITHMETIC_TYPE9 unsigned long |
---|
199 | #define ARITHMETIC_TYPE10 float |
---|
200 | #define ARITHMETIC_TYPE11 double |
---|
201 | #define ARITHMETIC_TYPE12 long double |
---|
202 | |
---|
203 | #define ARITHMETIC_TYPE_CNT 13 |
---|
204 | |
---|
205 | // ... |
---|
206 | |
---|
207 | #define BOOST_PP_DEF(z, I, _) /* ... */ \ |
---|
208 | catch (ARITHMETIC_TYPE(I) t) { \ |
---|
209 | report_typeid(t); \ |
---|
210 | report_value(t); \ |
---|
211 | } \ |
---|
212 | /**/ |
---|
213 | |
---|
214 | BOOST_PP_REPEAT(ARITHMETIC_TYPE_CNT, BOOST_PP_DEF, _) |
---|
215 | |
---|
216 | #undef BOOST_PP_DEF |
---|
217 | </pre></div> |
---|
218 | <div> |
---|
219 | <b>Note:</b> The repetition of the above example can be eliminated using template metaprogramming <a href="../bibliography.html#czarnecki">[Czarnecki]</a> as well. |
---|
220 | However categorical repetition of operator tokens cannot be completely eliminated by using template metaprogramming. |
---|
221 | </div> |
---|
222 | |
---|
223 | <h4>Example<u> - Use BOOST_PP_REPEAT to avoid <i>O</i>(<i>n</i>*<i>n</i>) repetition.</u></h4> |
---|
224 | <div class="code"><pre> |
---|
225 | #ifndef MAX_VEC_ARG_CNT |
---|
226 | #define MAX_VEC_ARG_CNT 8 |
---|
227 | #endif |
---|
228 | |
---|
229 | // ... |
---|
230 | |
---|
231 | #define ARG_FUN(z, i, _) BOOST_PP_COMMA_IF(i) T a ## i |
---|
232 | #define ASSIGN_FUN(z, i, ) (*this)[i] = a ## i; |
---|
233 | |
---|
234 | #define DEF_VEC_CTOR_FUN(z, i, _) /* ... */ \ |
---|
235 | vec(BOOST_PP_REPEAT(i, ARG_FUN, _)) { \ |
---|
236 | BOOST_PP_REPEAT(i, ASSIGN_FUN, _) \ |
---|
237 | } \ |
---|
238 | /**/ |
---|
239 | |
---|
240 | BOOST_PP_REPEAT(BOOST_PP_INC(MAX_VEC_ARG_CNT), DEF_VEC_CTOR_FUN, _) |
---|
241 | |
---|
242 | #undef ARG_FUN |
---|
243 | #undef ASSIGN_FUN |
---|
244 | #undef DEF_VEC_CTOR_FUN |
---|
245 | |
---|
246 | // ... |
---|
247 | </pre></div> |
---|
248 | <div> |
---|
249 | <b>How:</b> BOOST_PP_REPEAT is implemented is a special way to enable <i>automatic recursion</i>. |
---|
250 | </div> |
---|
251 | |
---|
252 | <h4>Example<u> - Use BOOST_PP_IF to implement special case for the first element.</u></h4> |
---|
253 | <div class="code"><pre> |
---|
254 | #define COMMA_IF(c) \ |
---|
255 | BOOST_PP_IF(c, BOOST_PP_COMMA, BOOST_PP_EMPTY)() \ |
---|
256 | /**/ |
---|
257 | |
---|
258 | BOOST_PP_IF(0, true, false) == false; |
---|
259 | BOOST_PP_IF(1, true, false) == true; |
---|
260 | </pre></div> |
---|
261 | <div> |
---|
262 | BOOST_PP_IF enables convenient generation of lists using BOOST_PP_REPEAT. |
---|
263 | </div> |
---|
264 | <div> |
---|
265 | <b>Note:</b> <i>THEN</i> and <i>ELSE</i> don't have to be macros. |
---|
266 | However, if at least one of them is a function-like macro, and you want it be expanded conditionally, |
---|
267 | you have to make the other parameter a function-like macro too. |
---|
268 | This can often be done using BOOST_PP_IDENTITY. |
---|
269 | Consider the following example (by Aleksey Gurtovoy): |
---|
270 | </div> |
---|
271 | <div><pre> |
---|
272 | #define NUMBERED_EXPRESSION(i, x) /* ... */ \ |
---|
273 | BOOST_PP_IF( \ |
---|
274 | i, \ |
---|
275 | BOOST_PP_IDENTITY(x ## i) \ |
---|
276 | BOOST_PP_EMPTY \ |
---|
277 | )() \ |
---|
278 | /**/ |
---|
279 | </pre></div> |
---|
280 | <div> |
---|
281 | <b>Note:</b> Like in the above implementation of COMMA_IF, the result of BOOST_PP_IF is often invoked and not the <i>THEN</i> and <i>ELSE</i> parameters. |
---|
282 | If the parameters were invoked, the code would not expand correctly, because the BOOST_PP_EMPTY parameter would get expanded |
---|
283 | to nothing before the <b>BOOST_PP_IF</b> would be properly expanded. |
---|
284 | </div> |
---|
285 | <div> |
---|
286 | <b>How:</b> BOOST_PP_IF is defined for the entire repeat range (psuedo code): |
---|
287 | </div> |
---|
288 | <div><pre> |
---|
289 | #define BOOST_PP_IF(c, THEN, ELSE) BOOST_PP_IF ## c(THEN, ELSE) |
---|
290 | |
---|
291 | #define BOOST_PP_IF0(THEN, ELSE) ELSE |
---|
292 | #define BOOST_PP_IF1(THEN, ELSE) THEN |
---|
293 | #define BOOST_PP_IF1(THEN, ELSE) THEN |
---|
294 | // ... |
---|
295 | </pre></div> |
---|
296 | |
---|
297 | <h4>Example:<u> Use arithmetic, logical, and comparison operations when necessary.</u></h4> |
---|
298 | <div class="code"><pre> |
---|
299 | #define SPECIAL_NUMBERED_LIST(n, i, elem, special) \ |
---|
300 | BOOST_PP_ASSERT_MSG( \ |
---|
301 | BOOST_PP_LESS(i, n), \ |
---|
302 | bad params for SPECIAL_NUMBERED_LIST! \ |
---|
303 | ) \ |
---|
304 | BOOST_PP_ENUM_PARAMS(i, elem) \ |
---|
305 | BOOST_PP_COMMA_IF(i) special \ |
---|
306 | BOOST_PP_REPEAT( \ |
---|
307 | BOOST_PP_SUB(BOOST_PP_DEC(n), i), \ |
---|
308 | SPECIAL_NUMBERED_LIST_HELPER, \ |
---|
309 | (elem, i) \ |
---|
310 | ) \ |
---|
311 | /**/ |
---|
312 | |
---|
313 | #define SPECIAL_NUMBERED_LIST_HELPER(z, i, elem_base) \ |
---|
314 | , \ |
---|
315 | BOOST_PP_CAT( \ |
---|
316 | BOOST_PP_TUPLE_ELEM(2, 0, elem_base), \ |
---|
317 | BOOST_PP_ADD( \ |
---|
318 | i, \ |
---|
319 | BOOST_PP_TUPLE_ELEM(2, 1, elem_base) \ |
---|
320 | ) \ |
---|
321 | ) \ |
---|
322 | /**/ |
---|
323 | |
---|
324 | SPECIAL_NUMBERED_LIST(3, 0, E, S) |
---|
325 | SPECIAL_NUMBERED_LIST(3, 1, E, S) |
---|
326 | SPECIAL_NUMBERED_LIST(3, 2, E, S) |
---|
327 | SPECIAL_NUMBERED_LIST(3, 3, E, S) |
---|
328 | </pre></div> |
---|
329 | |
---|
330 | <hr size="1"> |
---|
331 | <div style="margin-left: 0px;"> |
---|
332 | <i>© Copyright <a href="http://www.housemarque.com" target="_top">Housemarque Oy</a> 2002</i> |
---|
333 | </div> |
---|
334 | <div style="margin-left: 0px;"> |
---|
335 | Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies. |
---|
336 | This document is provided "as is" without express or implied warranty and with no claim as to its suitability for any purpose. |
---|
337 | </div> |
---|
338 | </body> |
---|
339 | </html> |
---|