Commit | Line | Data |
---|---|---|
b64eb60f MW |
1 | .\" -*-nroff-*- |
2 | .TH control 3 "23 April 2023" "Straylight/Edgeware" "mLib utilities library" | |
3 | .SH NAME | |
4 | control \- control structure metaprogramming | |
5 | .\" @MC_BEFORE | |
6 | .\" @MC_AFTER | |
7 | .\" @MC_WRAP | |
8 | .\" @MC_FINALLY | |
9 | .\" @MC_DOWHILE | |
10 | .\" @MC_DECL | |
11 | .\" @MC_LOOPELSE | |
12 | .\" @MC_LOOPBETWEEN | |
13 | .\" @MC_ALLOWELSE | |
14 | .\" @MC_GOELSE | |
15 | .\" @MC_TARGET | |
16 | .\" @MC_GOTARGET | |
17 | .\" @MC_ACT | |
18 | .\" @MC_LABEL | |
19 | .\" @MC_GOTO | |
20 | .SH SYNOPSIS | |
21 | .nf | |
22 | .B "#include <mLib/control.h>" | |
23 | ||
24 | .BI MC_BEFORE( tag ", " stmts ") " body | |
25 | .BI MC_AFTER( tag ", " stmts ") " body | |
26 | .BI MC_WRAP( tag ", " before_stmt ", " onend_stmt ", " onbreak_stmt ") " body | |
27 | .BI MC_DOWHILE( tag ", " cond ") " body | |
28 | .BI MC_DECL( tag ", " decl ") " body | |
29 | .BI MC_LOOPELSE( tag ", " head ") " loop_body " \fR[\fBelse " else_body \fR] | |
30 | ||
31 | .BI MC_TARGET( tag ", " stmt ") " body | |
32 | .BI MC_GOTARGET( tag ); | |
33 | .BI MC_ALLOWELSE( tag ") " main_body " \fR[\fBelse " else_body \fR] | |
34 | .BI MC_GOELSE( tag ); | |
35 | ||
36 | .BI MC_ACT( stmt ) | |
37 | .BI MC_LABEL( tag ) | |
38 | .BI MC_GOTO( tag ) | |
39 | .fi | |
40 | .SH DESCRIPTION | |
41 | The header file | |
42 | .B <mLib/control.h> | |
43 | defines a number of macros which are useful when defining new | |
44 | control structures for C. They are inspired by Simon Tatham's article | |
45 | .IR "Metaprogramming custom control structures in C", | |
46 | though these macros differ from Tatham's in a few respects. | |
47 | .SS "Common features" | |
48 | Each of these macros takes a | |
49 | .I tag | |
50 | argument. A | |
51 | .I tag | |
52 | is lexically like an identifier, except that it may begin with a digit, | |
53 | so, for example, plain integers are acceptable tags. Each use of an | |
54 | action macro by a user-level macro must have a distinct | |
55 | .IR tag . | |
56 | If you're writing a new prefix action macro written in terms of these | |
57 | existing actions, your macro should receive a | |
58 | .I tag | |
59 | from its caller, and pass this tag, along with a distinctive component | |
60 | of its own, down to any prefix actions that it calls; the | |
61 | .IR tag s | |
62 | from each layer should be separated by a pair of underscores. | |
63 | .PP | |
64 | Some of these macros work by wrapping a loop around the | |
65 | .I body | |
66 | statement. This interferes with the way that `free' | |
67 | .B break | |
68 | and | |
69 | .B continue | |
70 | statements within the | |
71 | .I body | |
72 | behave: we say that these statements are | |
73 | .I captured | |
74 | by the macro. | |
75 | A | |
76 | .B break | |
77 | or | |
78 | .B continue | |
79 | statement is `free' if it doesn't appear lexically within a loop or | |
80 | (for | |
81 | .B break) | |
82 | .B switch | |
83 | statement that is part of the | |
84 | .IR body . | |
85 | So | |
86 | .IP | |
87 | .B "if (!x) break;" | |
88 | .PP | |
89 | contains a free | |
90 | .B break | |
91 | statement, while | |
92 | .IP | |
93 | .nf | |
94 | .ft B | |
95 | for (i = 0; i < n; i++) | |
96 | \h'4n'if (interestingp(i)) break; | |
97 | .ft | |
98 | .fi | |
99 | .PP | |
100 | does not. | |
101 | .PP | |
102 | Some of these macros take special care to give you control over what | |
103 | happens when a captured | |
104 | .B break | |
105 | is executed. Alas, proper handling of | |
106 | .B continue | |
107 | doesn't seem possible. Free | |
108 | .B break | |
109 | and | |
110 | .B continue | |
111 | statements | |
112 | .I within | |
113 | arguments to these macros are never captured. | |
114 | .SS "Prefix action macros" | |
115 | .B MC_BEFORE | |
116 | macro is the simplest to understand. Executing | |
117 | .IP | |
118 | .BI MC_BEFORE( tag ", " stmt ") " body | |
119 | .PP | |
120 | has the same effect as executing | |
121 | .I stmt | |
122 | followed by | |
123 | .IR body , | |
124 | except that the whole thing is syntactically a single statement, so, for | |
125 | example, it doesn't need to be enclosed in braces to be the body of a | |
126 | .B for | |
127 | loop. | |
128 | .B MC_BEFORE | |
129 | does not capture free | |
130 | .B break | |
131 | or | |
132 | .B continue | |
133 | statements. | |
134 | .PP | |
135 | Executing | |
136 | .IP | |
137 | .BI MC_AFTER( tag ", " stmt ") " body | |
138 | .PP | |
139 | has | |
140 | .I nearly | |
141 | the same effect as executing | |
142 | .I stmt | |
143 | followed by | |
144 | .IR body . | |
145 | Again, the whole thing is syntactically a single statement. However, | |
146 | .B MC_AFTER | |
147 | captures free | |
148 | .B break | |
149 | and | |
150 | .B continue | |
151 | statements within the | |
152 | .IR body . | |
153 | A free | |
154 | .B break | |
155 | or | |
156 | .B continue | |
157 | abruptly ends execution of the | |
158 | .IR body , | |
159 | immediately transferring control to the | |
160 | .IR stmt . | |
161 | .PP | |
162 | Executing | |
163 | .IP | |
164 | .BI MC_WRAP( tag ", " before ", " onend ", " onbreak ") " body | |
165 | .PP | |
166 | has | |
167 | .I nearly | |
168 | the same effect as executing | |
169 | .IR before , | |
170 | .IR body , | |
171 | and then | |
172 | .IR onend . | |
173 | If the | |
174 | .I body | |
175 | executes a free | |
176 | .B break | |
177 | statement, then control abruptly continues with the | |
178 | .I onbreak | |
179 | statement, and | |
180 | .I onend | |
181 | is not executed. Currently, if the | |
182 | .I body | |
183 | executes a free | |
184 | .B continue | |
185 | statement, then control abruptly continues with the | |
186 | .I onend | |
187 | statement, but this behaviour is a bug and may be fixed in the future. | |
188 | .PP | |
189 | .\" @@@ mc_finally | |
190 | .PP | |
191 | Executing | |
192 | .IP | |
193 | .BI MC_DOWHILE( tag ", " cond ") " body | |
194 | .PP | |
195 | has exactly the same effect as | |
196 | .BI "do " body " while (" cond ); \fR, | |
197 | the only difference being that the | |
198 | .I body | |
199 | appears in tail position rather than sandwiched in the middle. | |
200 | .PP | |
201 | Executing | |
202 | .BI MC_DECL( tag ", " decl ") " body | |
203 | has the same effect as | |
204 | .BI "{ " decl "; " body " }" \fR, | |
205 | except that free | |
206 | .B break | |
207 | and | |
208 | .B continue | |
209 | statements are captured. Currently, a free | |
210 | .B continue | |
211 | statement will simply abruptly terminate execution of the | |
212 | .IR body , | |
213 | while a free | |
214 | .B break | |
215 | statement abruptly | |
216 | .I restarts | |
217 | executing the | |
218 | .I body | |
219 | without leaving the scope of the | |
220 | .IR decl ; | |
221 | but these behaviours are bugs and may be fixed in the future. | |
222 | .PP | |
223 | The | |
224 | .B MC_DECL | |
225 | macro makes use of the fact that a | |
226 | .B for | |
227 | statement can introduce a declaration into its body's scope in C99 and | |
228 | C++; the macro is not available in C89. | |
229 | .PP | |
230 | Executing | |
231 | .IP | |
232 | .nf | |
233 | .BI MC_LOOPELSE( head ", " tag ") " | |
234 | .RI \h'4n' loop_body | |
235 | .RB [ else | |
236 | .RI \h'4n' else_body ] | |
237 | .fi | |
238 | .PP | |
239 | results in Python-like loop behaviour. The | |
240 | .I head | |
241 | must be a valid loop head with one of the forms | |
242 | .IP | |
243 | .BI "while (" cond ")" | |
244 | .br | |
245 | .BI "for (" decl "; " cond "; " next_expr ")" | |
246 | .br | |
247 | .BI "MC_DOWHILE(" tag ", " cond ")" | |
248 | .PP | |
249 | The resulting loop executes the same as | |
250 | .IP | |
251 | .nf | |
252 | .I head | |
253 | .RI \h'4n' loop_body | |
254 | .fi | |
255 | .PP | |
256 | If the loop ends abruptly, as a result of | |
257 | .BR break , | |
258 | then control is passed to the statement following the loop in the usual | |
259 | way. However, if the loop completes naturally, and the optional | |
260 | .B else | |
261 | clause is present, then the | |
262 | .I else_body | |
263 | is executed. A free | |
264 | .B continue | |
265 | statement within the | |
266 | .I loop_body | |
267 | behaves normally. Free | |
268 | .B break | |
269 | and | |
270 | .B continue | |
271 | statements within the | |
272 | .I else_body | |
273 | are not captured. | |
274 | .PP | |
275 | .\" @@@ loopbetween | |
276 | .SS "Lower-level machinery" | |
277 | Executing | |
278 | .IP | |
279 | .BI MC_TARGET( tag ", " stmt ") " body | |
280 | .PP | |
281 | has exactly the same effect as simply executing | |
282 | .IR body . | |
283 | Executing | |
284 | .B MC_GOTARGET | |
285 | immediately transfers control to | |
286 | .IR stmt , | |
287 | with control continuing with the following statement, skipping the | |
288 | .IR body . | |
289 | Free | |
290 | .B break | |
291 | or | |
292 | .B continue | |
293 | statements in | |
294 | .I body | |
295 | are not captured. | |
296 | .PP | |
297 | This is most commonly useful in loops in order to arrange the correct | |
298 | behaviour of a free | |
299 | .B break | |
300 | within the loop body. See the example below, which shows the definition | |
301 | of | |
302 | .BR MC_LOOPELSE . | |
303 | .PP | |
304 | Executing | |
305 | .IP | |
306 | .BI MC_ALLOWELSE( tag ") " main_body " \fR[\fBelse " else_body \fR] | |
307 | .PP | |
308 | has exactly the same effect as just | |
309 | .IR main_body . | |
310 | Executing | |
311 | .IP | |
312 | .BI MC_GOELSE( tag ); | |
313 | .PP | |
314 | transfers control immediately to | |
315 | .I else_body | |
316 | (if present); control then naturally transfers to the following | |
317 | statement as usual. Free | |
318 | .B break | |
319 | or | |
320 | .B continue | |
321 | statements in either of | |
322 | .I main_body | |
323 | or | |
324 | .I else_body | |
325 | are not captured. | |
326 | .PP | |
327 | Note that | |
328 | .B MC_ALLOWELSE | |
329 | works by secretly inserting an | |
330 | .B if | |
331 | statement head before the | |
332 | .IR main_body , | |
333 | so things will likely to wrong if | |
334 | .I main_body | |
335 | is itself an | |
336 | .B if | |
337 | statement: if | |
338 | .I main_body | |
339 | lacks an | |
340 | .B else | |
341 | clause, then an | |
342 | .B else | |
343 | intended to match | |
344 | .B MC_ALLOWELSE | |
345 | will be mis-associated; and even if | |
346 | .I main_body | |
347 | .I does | |
348 | have an | |
349 | .B else | |
350 | clause, the resulting program text is likely to provoke a compiler | |
351 | warning about `dangling | |
352 | .BR else '. | |
353 | .PP | |
354 | Using these tools, it's relatively straightforward to define a macro | |
355 | like | |
356 | .BR MC_LOOPELSE , | |
357 | described above: | |
358 | .IP | |
359 | .nf | |
360 | .ft B | |
361 | #define MC_LOOPELSE(tag, head) \e | |
362 | \h'4n'MC_TARGET(tag##__exit, { ; }) \e | |
363 | \h'4n'MC_ALLOWELSE(tag##__else) \e | |
364 | \h'4n'MC_AFTER(tag##__after, { MC_GOELSE(tag##__else); }) \e | |
365 | \h'4n'head \e | |
366 | \h'8n'MC_WRAP(tag##__body, { ; }, { ; }, \e | |
367 | \h'8n+\w'MC_WRAP(tag##__body, ''{ MC_GOTARGET(tag##__exit); }) | |
368 | .ft R | |
369 | .fi | |
370 | .PP | |
371 | The main `trick' for these control-flow macros is | |
372 | .BR MC_ACT , | |
373 | which wraps up a statement as an | |
374 | .IR action . | |
375 | An action is a valid | |
376 | .IR "statement head" , | |
377 | like | |
378 | .B if | |
379 | or | |
380 | .BR while : | |
381 | i.e., it must be completed by following it with a | |
382 | .I body | |
383 | statement. Executing | |
384 | .IP | |
385 | .BI MC_ACT( stmt ") " body | |
386 | .PP | |
387 | has the same effect as simply executing | |
388 | .IR stmt ; | |
389 | the | |
390 | .I body | |
391 | is usually ignored. Note that | |
392 | .B ; | |
393 | is a valid statement which does nothing, so | |
394 | .BI MC_ACT( stmt ); | |
395 | is also a valid statement with the same effect as | |
396 | .IR stmt . | |
397 | The only way to cause | |
398 | .I body | |
399 | to be executed is to attach a label to it and transfer control using | |
400 | .BR goto . | |
401 | .PP | |
402 | Executing | |
403 | .IP | |
404 | .BI MC_LABEL( tag ") " body | |
405 | .PP | |
406 | has the same effect as | |
407 | .IR body . | |
408 | Executing | |
409 | .IP | |
410 | .BI MC_GOTO( tag ) | |
411 | .PP | |
412 | immediately transfers control to the | |
413 | .IR body . | |
414 | Note that | |
415 | .B MC_GOTO | |
416 | is syntactically an action | |
417 | (i.e., it's wrapped in | |
418 | .BR MC_ACT ). | |
419 | The | |
420 | .IR tag s | |
421 | here are scoped to the top-level source line, like all | |
422 | .IR tag s | |
423 | in this macro package. | |
424 | .PP | |
425 | All of the control-flow macros in this package are mainly constructed from | |
426 | .BR MC_ACT , | |
427 | .BR MC_LABEL , | |
428 | and | |
429 | .BR MC_GOTO , | |
430 | sometimes with one or two other statement heads thrown into the mix. | |
431 | For example, | |
432 | .B MC_AFTER | |
433 | is defined as | |
434 | .IP | |
435 | .nf | |
436 | .ft B | |
437 | #define MC_AFTER(tag, stmt) \e | |
438 | \h'28n'MC_GOTO(tag##__body) \e | |
439 | \h'4n'MC_LABEL(tag##__end) \e | |
440 | \h'28n'MC_ACT(stmt) \e | |
441 | \h'28n'for (;;) \e | |
442 | \h'32n'MC_GOTO(tag##__end) \e | |
443 | \h'4n'MC_LABEL(tag##__body) | |
444 | .ft R | |
445 | .fi | |
446 | .PP | |
447 | (The unusual layout is conventional, to make the overall structure of | |
448 | the code clear despite visual interference from the labels.) | |
449 | The | |
450 | .I body | |
451 | appears at the end, labelled as | |
452 | .IB tag __body \fR. | |
453 | Control enters at the start, and is immediately transferred to the | |
454 | .I body ; | |
455 | but the | |
456 | .I body | |
457 | is enclosed in a | |
458 | .B for | |
459 | loop, so when the | |
460 | .I body | |
461 | completes, the loop restarts, transferring control to | |
462 | .IB tag __end | |
463 | and the | |
464 | .IR stmt . | |
465 | Since it is enclosed in | |
466 | .BR MC_ACT , | |
467 | once | |
468 | .I stmt | |
469 | completes, control transfers to the following statement. | |
470 | .SH BUGS | |
471 | Some macros cause free | |
472 | .B break | |
473 | and/or | |
474 | .B continue | |
475 | statements to behave in unexpected ways. | |
476 | .PP | |
477 | The need for tagging is ugly, and the restriction on having two | |
478 | user-facing control-flow macros on the same line is objectionable. The | |
479 | latter could be avoided by using nonstandard features such as GCC's | |
480 | .B __COUNTER__ | |
481 | macro, but adopting that would do programmers a disservice by | |
482 | introducing a hazard for those trying to port code to other compilers | |
483 | which lack any such feature. | |
484 | .SH "SEE ALSO" | |
485 | .BR mLib (3), | |
486 | .BR macros (3). | |
487 | .PP | |
488 | Simon Tatham, | |
489 | .IR "Metaprogramming custom control structures in C", | |
490 | .BR "https://www.chiark.greenend.org.uk/~sgtatham/mp/" . | |
491 | .SH "AUTHOR" | |
492 | Mark Wooding, <mdw@distorted.org.uk> |