Commit | Line | Data |
---|---|---|
b64eb60f | 1 | .\" -*-nroff-*- |
adec5584 MW |
2 | .de VS |
3 | .sp 1 | |
4 | .RS | |
5 | .nf | |
6 | .ft B | |
7 | .. | |
8 | .de VE | |
9 | .ft R | |
10 | .fi | |
11 | .RE | |
12 | .sp 1 | |
13 | .. | |
b64eb60f | 14 | .TH control 3 "23 April 2023" "Straylight/Edgeware" "mLib utilities library" |
adec5584 | 15 | . |
b64eb60f MW |
16 | .SH NAME |
17 | control \- control structure metaprogramming | |
18 | .\" @MC_BEFORE | |
19 | .\" @MC_AFTER | |
20 | .\" @MC_WRAP | |
21 | .\" @MC_FINALLY | |
22 | .\" @MC_DOWHILE | |
23 | .\" @MC_DECL | |
24 | .\" @MC_LOOPELSE | |
25 | .\" @MC_LOOPBETWEEN | |
26 | .\" @MC_ALLOWELSE | |
27 | .\" @MC_GOELSE | |
28 | .\" @MC_TARGET | |
29 | .\" @MC_GOTARGET | |
30 | .\" @MC_ACT | |
31 | .\" @MC_LABEL | |
32 | .\" @MC_GOTO | |
adec5584 | 33 | . |
b64eb60f MW |
34 | .SH SYNOPSIS |
35 | .nf | |
36 | .B "#include <mLib/control.h>" | |
d056fbdf | 37 | .PP |
b64eb60f MW |
38 | .BI MC_BEFORE( tag ", " stmts ") " body |
39 | .BI MC_AFTER( tag ", " stmts ") " body | |
40 | .BI MC_WRAP( tag ", " before_stmt ", " onend_stmt ", " onbreak_stmt ") " body | |
adec5584 | 41 | .BI MC_FINALLY( tag ", " cleanup ") " body |
b64eb60f MW |
42 | .BI MC_DOWHILE( tag ", " cond ") " body |
43 | .BI MC_DECL( tag ", " decl ") " body | |
44 | .BI MC_LOOPELSE( tag ", " head ") " loop_body " \fR[\fBelse " else_body \fR] | |
adec5584 | 45 | .BI MC_LOOPBETWEEN( tag ", " setup ", " cond ", " step ") " loop_body " \fR[\fBelse " else_body \fR] |
d056fbdf | 46 | .PP |
b64eb60f MW |
47 | .BI MC_TARGET( tag ", " stmt ") " body |
48 | .BI MC_GOTARGET( tag ); | |
49 | .BI MC_ALLOWELSE( tag ") " main_body " \fR[\fBelse " else_body \fR] | |
50 | .BI MC_GOELSE( tag ); | |
d056fbdf | 51 | .PP |
b64eb60f MW |
52 | .BI MC_ACT( stmt ) |
53 | .BI MC_LABEL( tag ) | |
54 | .BI MC_GOTO( tag ) | |
55 | .fi | |
adec5584 | 56 | . |
b64eb60f MW |
57 | .SH DESCRIPTION |
58 | The header file | |
59 | .B <mLib/control.h> | |
adec5584 MW |
60 | defines a number of macros which are useful |
61 | when defining new control structures for C. | |
62 | They are inspired by Simon Tatham's article | |
b64eb60f MW |
63 | .IR "Metaprogramming custom control structures in C", |
64 | though these macros differ from Tatham's in a few respects. | |
adec5584 | 65 | . |
b64eb60f MW |
66 | .SS "Common features" |
67 | Each of these macros takes a | |
68 | .I tag | |
adec5584 MW |
69 | argument. |
70 | A | |
b64eb60f | 71 | .I tag |
adec5584 MW |
72 | is lexically like an identifier, |
73 | except that it may begin with a digit, | |
74 | so, for example, plain integers are acceptable tags. | |
75 | Each use of an action macro by a user-level macro | |
76 | must have a distinct | |
b64eb60f | 77 | .IR tag . |
adec5584 MW |
78 | If you're writing a new prefix action macro |
79 | written in terms of these existing actions, | |
80 | your macro should receive a | |
b64eb60f | 81 | .I tag |
adec5584 MW |
82 | from its caller, |
83 | and pass this tag, | |
84 | along with a distinctive component of its own, | |
85 | down to any prefix actions that it calls; | |
86 | the | |
b64eb60f MW |
87 | .IR tag s |
88 | from each layer should be separated by a pair of underscores. | |
89 | .PP | |
90 | Some of these macros work by wrapping a loop around the | |
91 | .I body | |
adec5584 MW |
92 | statement. |
93 | This interferes with the way that `free' | |
b64eb60f MW |
94 | .B break |
95 | and | |
96 | .B continue | |
97 | statements within the | |
98 | .I body | |
adec5584 MW |
99 | behave: |
100 | we say that these statements are | |
b64eb60f MW |
101 | .I captured |
102 | by the macro. | |
103 | A | |
104 | .B break | |
105 | or | |
106 | .B continue | |
adec5584 MW |
107 | statement is |
108 | .I free | |
109 | if it doesn't appear lexically within a loop or | |
b64eb60f MW |
110 | (for |
111 | .B break) | |
112 | .B switch | |
113 | statement that is part of the | |
114 | .IR body . | |
115 | So | |
adec5584 MW |
116 | .VS |
117 | if (!x) break; | |
118 | .VE | |
b64eb60f MW |
119 | contains a free |
120 | .B break | |
adec5584 MW |
121 | statement, |
122 | while | |
123 | .VS | |
124 | .ta 2n | |
b64eb60f | 125 | for (i = 0; i < n; i++) |
adec5584 MW |
126 | if (interestingp(i)) break; |
127 | .VE | |
b64eb60f MW |
128 | does not. |
129 | .PP | |
adec5584 MW |
130 | Some of these macros take special care |
131 | to give you control over what happens when a captured | |
b64eb60f | 132 | .B break |
adec5584 MW |
133 | is executed. |
134 | Alas, proper handling of | |
b64eb60f | 135 | .B continue |
adec5584 MW |
136 | doesn't seem possible. |
137 | Free | |
b64eb60f MW |
138 | .B break |
139 | and | |
140 | .B continue | |
141 | statements | |
142 | .I within | |
143 | arguments to these macros are never captured. | |
adec5584 | 144 | . |
b64eb60f MW |
145 | .SS "Prefix action macros" |
146 | .B MC_BEFORE | |
147 | macro is the simplest to understand. Executing | |
148 | .IP | |
149 | .BI MC_BEFORE( tag ", " stmt ") " body | |
150 | .PP | |
151 | has the same effect as executing | |
152 | .I stmt | |
153 | followed by | |
154 | .IR body , | |
adec5584 MW |
155 | except that the whole thing is syntactically a single statement, |
156 | so, for example, it doesn't need to be enclosed in braces | |
157 | to be the body of a | |
b64eb60f MW |
158 | .B for |
159 | loop. | |
160 | .B MC_BEFORE | |
161 | does not capture free | |
162 | .B break | |
163 | or | |
164 | .B continue | |
165 | statements. | |
166 | .PP | |
167 | Executing | |
168 | .IP | |
169 | .BI MC_AFTER( tag ", " stmt ") " body | |
170 | .PP | |
171 | has | |
172 | .I nearly | |
173 | the same effect as executing | |
174 | .I stmt | |
175 | followed by | |
176 | .IR body . | |
adec5584 MW |
177 | Again, the whole thing is syntactically a single statement. |
178 | However, | |
b64eb60f MW |
179 | .B MC_AFTER |
180 | captures free | |
181 | .B break | |
182 | and | |
183 | .B continue | |
184 | statements within the | |
185 | .IR body . | |
186 | A free | |
187 | .B break | |
188 | or | |
189 | .B continue | |
190 | abruptly ends execution of the | |
191 | .IR body , | |
192 | immediately transferring control to the | |
193 | .IR stmt . | |
194 | .PP | |
195 | Executing | |
196 | .IP | |
197 | .BI MC_WRAP( tag ", " before ", " onend ", " onbreak ") " body | |
198 | .PP | |
199 | has | |
200 | .I nearly | |
201 | the same effect as executing | |
202 | .IR before , | |
203 | .IR body , | |
204 | and then | |
205 | .IR onend . | |
206 | If the | |
207 | .I body | |
208 | executes a free | |
209 | .B break | |
210 | statement, then control abruptly continues with the | |
211 | .I onbreak | |
212 | statement, and | |
213 | .I onend | |
adec5584 MW |
214 | is not executed. |
215 | Currently, if the | |
b64eb60f MW |
216 | .I body |
217 | executes a free | |
218 | .B continue | |
adec5584 MW |
219 | statement, |
220 | then control abruptly continues with the | |
b64eb60f | 221 | .I onend |
adec5584 MW |
222 | statement, |
223 | but this behaviour is a bug and may be fixed in the future. | |
224 | .PP | |
225 | Executing | |
226 | .IP | |
227 | .BI MC_FINALLY( tag ", " cleanup ") " body | |
b64eb60f | 228 | .PP |
adec5584 MW |
229 | has the same effect as executing |
230 | .I body | |
231 | followed by | |
232 | .IR cleanup , | |
233 | except that a free | |
234 | .B break | |
235 | statement within | |
236 | .I body | |
237 | will execute | |
238 | .I cleanup | |
239 | before propagating the | |
240 | .B break | |
241 | to the enclosing context. | |
242 | A free | |
243 | .B continue | |
244 | statement currently causes control to continue abruptly with | |
245 | .I cleanup | |
246 | but this behaviour is a bug and may be fixed in the future. | |
247 | The | |
248 | .I cleanup | |
249 | code is textually duplicated, | |
250 | so there'll be some code bloat if this is very complex. | |
251 | If it arranges to have private long-term state | |
252 | then the two copies will not share this state, | |
253 | so probably don't do this. | |
b64eb60f MW |
254 | .PP |
255 | Executing | |
256 | .IP | |
257 | .BI MC_DOWHILE( tag ", " cond ") " body | |
258 | .PP | |
259 | has exactly the same effect as | |
260 | .BI "do " body " while (" cond ); \fR, | |
261 | the only difference being that the | |
262 | .I body | |
263 | appears in tail position rather than sandwiched in the middle. | |
264 | .PP | |
265 | Executing | |
266 | .BI MC_DECL( tag ", " decl ") " body | |
267 | has the same effect as | |
268 | .BI "{ " decl "; " body " }" \fR, | |
269 | except that free | |
270 | .B break | |
271 | and | |
272 | .B continue | |
adec5584 MW |
273 | statements are captured. |
274 | Currently, a free | |
b64eb60f MW |
275 | .B continue |
276 | statement will simply abruptly terminate execution of the | |
277 | .IR body , | |
278 | while a free | |
279 | .B break | |
280 | statement abruptly | |
281 | .I restarts | |
282 | executing the | |
283 | .I body | |
284 | without leaving the scope of the | |
285 | .IR decl ; | |
286 | but these behaviours are bugs and may be fixed in the future. | |
287 | .PP | |
288 | The | |
289 | .B MC_DECL | |
290 | macro makes use of the fact that a | |
291 | .B for | |
adec5584 MW |
292 | statement can introduce a declaration |
293 | into its body's scope in C99 and C++; | |
294 | the macro is not available in C89. | |
b64eb60f MW |
295 | .PP |
296 | Executing | |
297 | .IP | |
298 | .nf | |
adec5584 | 299 | .ta 2n |
b64eb60f | 300 | .BI MC_LOOPELSE( head ", " tag ") " |
adec5584 | 301 | .I " loop_body" |
b64eb60f | 302 | .RB [ else |
adec5584 | 303 | .IR " else_body" ] |
b64eb60f MW |
304 | .fi |
305 | .PP | |
adec5584 MW |
306 | results in Python-like loop behaviour. |
307 | The | |
b64eb60f MW |
308 | .I head |
309 | must be a valid loop head with one of the forms | |
310 | .IP | |
adec5584 | 311 | .nf |
b64eb60f | 312 | .BI "while (" cond ")" |
b64eb60f | 313 | .BI "for (" decl "; " cond "; " next_expr ")" |
b64eb60f | 314 | .BI "MC_DOWHILE(" tag ", " cond ")" |
adec5584 | 315 | .fi |
b64eb60f MW |
316 | .PP |
317 | The resulting loop executes the same as | |
318 | .IP | |
319 | .nf | |
adec5584 | 320 | .ta 2n |
b64eb60f | 321 | .I head |
adec5584 | 322 | .I " loop_body" |
b64eb60f MW |
323 | .fi |
324 | .PP | |
325 | If the loop ends abruptly, as a result of | |
326 | .BR break , | |
adec5584 MW |
327 | then control is passed to the statement following the loop |
328 | in the usual way. | |
329 | However, if the loop completes naturally, | |
330 | and the optional | |
b64eb60f | 331 | .B else |
adec5584 MW |
332 | clause is present, |
333 | then the | |
b64eb60f | 334 | .I else_body |
adec5584 MW |
335 | is executed. |
336 | A free | |
b64eb60f MW |
337 | .B continue |
338 | statement within the | |
339 | .I loop_body | |
adec5584 MW |
340 | behaves normally. |
341 | Free | |
b64eb60f MW |
342 | .B break |
343 | and | |
344 | .B continue | |
345 | statements within the | |
346 | .I else_body | |
347 | are not captured. | |
348 | .PP | |
adec5584 MW |
349 | Executing |
350 | .IP | |
351 | .nf | |
352 | .ta 2n | |
353 | .BI MC_LOOPBETWEEN( tag ", " setup ", " cond ", " step ") " | |
354 | .I " loop-body" | |
355 | .RB [ else | |
356 | .IR " else-body" ] | |
357 | .fi | |
358 | .PP | |
359 | is similar to executing the | |
360 | .B for | |
361 | loop | |
362 | .IP | |
363 | .ta 2n | |
364 | .nf | |
365 | .BI "for (" setup "; " cond "; " step ") " | |
366 | .I " loop-body" | |
367 | .fi | |
368 | .PP | |
369 | except that, once the | |
370 | .I loop_body | |
371 | has finished, | |
372 | the | |
373 | .I step | |
374 | expression evaluated, | |
375 | and the | |
376 | .I cond | |
377 | evaluated and determined to be nonzero, | |
378 | the | |
379 | .I else_body | |
380 | (if any) is executed before re-entering the | |
381 | .IR loop_body . | |
382 | This makes it a useful place to insert | |
383 | any kind of interstitial material, | |
384 | e.g., printing commas between list items. | |
385 | Note that by the time the | |
386 | .I else_body | |
387 | is executed, | |
388 | the decision has already been made | |
389 | that another iteration will be performed, | |
390 | and, in particular, the | |
391 | .I step | |
392 | has occurred. The | |
393 | .I else_body | |
394 | is therefore looking at the next item to be processed, | |
395 | not the item that has just finished being processed. | |
396 | The | |
397 | .I cond | |
398 | is textually duplicated, | |
399 | so there'll be some code bloat if this is very complex. | |
400 | If it somehow manages to have private long-term state | |
401 | (e.g., as a result of declaring static variables | |
402 | inside GCC statement expressions) | |
403 | then the two copies will not share this state, | |
404 | so probably don't do this. | |
405 | . | |
b64eb60f MW |
406 | .SS "Lower-level machinery" |
407 | Executing | |
408 | .IP | |
409 | .BI MC_TARGET( tag ", " stmt ") " body | |
410 | .PP | |
411 | has exactly the same effect as simply executing | |
412 | .IR body . | |
413 | Executing | |
414 | .B MC_GOTARGET | |
415 | immediately transfers control to | |
416 | .IR stmt , | |
adec5584 MW |
417 | with control continuing with the following statement, |
418 | skipping the | |
b64eb60f MW |
419 | .IR body . |
420 | Free | |
421 | .B break | |
422 | or | |
423 | .B continue | |
424 | statements in | |
425 | .I body | |
426 | are not captured. | |
427 | .PP | |
adec5584 MW |
428 | This is most commonly useful in loops |
429 | in order to arrange the correct behaviour of a free | |
b64eb60f | 430 | .B break |
adec5584 MW |
431 | within the loop body. |
432 | See the example below, | |
433 | which shows the definition | |
b64eb60f MW |
434 | of |
435 | .BR MC_LOOPELSE . | |
436 | .PP | |
437 | Executing | |
438 | .IP | |
adec5584 MW |
439 | .nf |
440 | .ta 2n | |
441 | .BI MC_ALLOWELSE( tag ") " | |
442 | .I " main_body" | |
443 | .RB [ else | |
444 | .IR " else_body" ] | |
445 | .fi | |
b64eb60f MW |
446 | .PP |
447 | has exactly the same effect as just | |
448 | .IR main_body . | |
449 | Executing | |
450 | .IP | |
451 | .BI MC_GOELSE( tag ); | |
452 | .PP | |
453 | transfers control immediately to | |
454 | .I else_body | |
adec5584 MW |
455 | (if present); |
456 | control then naturally transfers to the following statement as usual. | |
457 | Free | |
b64eb60f MW |
458 | .B break |
459 | or | |
460 | .B continue | |
461 | statements in either of | |
462 | .I main_body | |
463 | or | |
464 | .I else_body | |
465 | are not captured. | |
466 | .PP | |
467 | Note that | |
468 | .B MC_ALLOWELSE | |
469 | works by secretly inserting an | |
470 | .B if | |
471 | statement head before the | |
472 | .IR main_body , | |
473 | so things will likely to wrong if | |
474 | .I main_body | |
475 | is itself an | |
476 | .B if | |
adec5584 MW |
477 | statement: |
478 | if | |
b64eb60f MW |
479 | .I main_body |
480 | lacks an | |
481 | .B else | |
adec5584 MW |
482 | clause, |
483 | then an | |
b64eb60f MW |
484 | .B else |
485 | intended to match | |
486 | .B MC_ALLOWELSE | |
adec5584 MW |
487 | will be mis-associated; |
488 | and even if | |
b64eb60f MW |
489 | .I main_body |
490 | .I does | |
491 | have an | |
492 | .B else | |
adec5584 MW |
493 | clause, |
494 | the resulting program text is likely to provoke a compiler warning | |
495 | about `dangling | |
b64eb60f MW |
496 | .BR else '. |
497 | .PP | |
adec5584 MW |
498 | Using these tools, |
499 | it's relatively straightforward to define a macro like | |
b64eb60f MW |
500 | .BR MC_LOOPELSE , |
501 | described above: | |
adec5584 MW |
502 | .VS |
503 | .ta 4n 4n+\w'\fBMC_WRAP(tag##__body, 'u \n(.lu-\n(.iu-4n | |
504 | #define MC_LOOPELSE(tag, head) \e | |
505 | MC_TARGET(tag##__exit, { ; }) \e | |
506 | MC_ALLOWELSE(tag##__else) \e | |
507 | MC_AFTER(tag##__after, { MC_GOELSE(tag##__else); }) \e | |
508 | head \e | |
509 | MC_WRAP(tag##__body, { ; }, { ; }, \e | |
510 | { MC_GOTARGET(tag##__exit); }) | |
511 | .VE | |
b64eb60f MW |
512 | The main `trick' for these control-flow macros is |
513 | .BR MC_ACT , | |
514 | which wraps up a statement as an | |
515 | .IR action . | |
516 | An action is a valid | |
517 | .IR "statement head" , | |
518 | like | |
519 | .B if | |
520 | or | |
521 | .BR while : | |
522 | i.e., it must be completed by following it with a | |
523 | .I body | |
adec5584 MW |
524 | statement. |
525 | Executing | |
b64eb60f MW |
526 | .IP |
527 | .BI MC_ACT( stmt ") " body | |
528 | .PP | |
529 | has the same effect as simply executing | |
530 | .IR stmt ; | |
531 | the | |
532 | .I body | |
adec5584 MW |
533 | is usually ignored. |
534 | Note that | |
b64eb60f | 535 | .B ; |
adec5584 MW |
536 | is a valid statement which does nothing, |
537 | so | |
b64eb60f MW |
538 | .BI MC_ACT( stmt ); |
539 | is also a valid statement with the same effect as | |
540 | .IR stmt . | |
541 | The only way to cause | |
542 | .I body | |
543 | to be executed is to attach a label to it and transfer control using | |
544 | .BR goto . | |
545 | .PP | |
546 | Executing | |
547 | .IP | |
548 | .BI MC_LABEL( tag ") " body | |
549 | .PP | |
550 | has the same effect as | |
551 | .IR body . | |
552 | Executing | |
553 | .IP | |
554 | .BI MC_GOTO( tag ) | |
555 | .PP | |
556 | immediately transfers control to the | |
557 | .IR body . | |
558 | Note that | |
559 | .B MC_GOTO | |
adec5584 MW |
560 | is syntactically an action, |
561 | i.e., it's wrapped in | |
562 | .BR MC_ACT . | |
b64eb60f MW |
563 | The |
564 | .IR tag s | |
adec5584 MW |
565 | here are scoped to the top-level source line, |
566 | like all | |
b64eb60f MW |
567 | .IR tag s |
568 | in this macro package. | |
569 | .PP | |
570 | All of the control-flow macros in this package are mainly constructed from | |
571 | .BR MC_ACT , | |
572 | .BR MC_LABEL , | |
573 | and | |
574 | .BR MC_GOTO , | |
575 | sometimes with one or two other statement heads thrown into the mix. | |
576 | For example, | |
577 | .B MC_AFTER | |
578 | is defined as | |
adec5584 MW |
579 | .VS |
580 | .ta 4n 28n 30n \n(.lu-\n(.iu-4n | |
581 | #define MC_AFTER(tag, stmt) \e | |
582 | MC_GOTO(tag##__body) \e | |
583 | MC_LABEL(tag##__end) \e | |
584 | MC_ACT(stmt) \e | |
585 | for (;;) \e | |
586 | MC_GOTO(tag##__end) \e | |
587 | MC_LABEL(tag##__body) | |
588 | .VE | |
589 | (The unusual layout is conventional, | |
590 | to make the overall structure of the code clear | |
591 | despite visual interference from the labels.) | |
b64eb60f MW |
592 | The |
593 | .I body | |
adec5584 MW |
594 | appears at the end, |
595 | labelled as | |
b64eb60f | 596 | .IB tag __body \fR. |
adec5584 MW |
597 | Control enters at the start, |
598 | and is immediately transferred to the | |
b64eb60f MW |
599 | .I body ; |
600 | but the | |
601 | .I body | |
602 | is enclosed in a | |
603 | .B for | |
604 | loop, so when the | |
605 | .I body | |
adec5584 MW |
606 | completes, the loop restarts, |
607 | transferring control to | |
b64eb60f MW |
608 | .IB tag __end |
609 | and the | |
610 | .IR stmt . | |
611 | Since it is enclosed in | |
612 | .BR MC_ACT , | |
613 | once | |
614 | .I stmt | |
adec5584 MW |
615 | completes, |
616 | control transfers to the following statement. | |
617 | . | |
618 | .SH "BUGS" | |
b64eb60f MW |
619 | Some macros cause free |
620 | .B break | |
621 | and/or | |
622 | .B continue | |
623 | statements to behave in unexpected ways. | |
624 | .PP | |
adec5584 MW |
625 | It's rather hard to use |
626 | .B MC_ALLOWELSE | |
627 | in practice without provoking | |
628 | .RB `dangling- else ' | |
629 | warnings. | |
630 | .PP | |
631 | The need for tagging is ugly, | |
632 | and the restriction on having two | |
633 | user-facing control-flow macros on the same line is objectionable. | |
634 | The latter could be avoided | |
635 | by using nonstandard features such as GCC's | |
b64eb60f | 636 | .B __COUNTER__ |
adec5584 MW |
637 | macro, |
638 | but adopting that would do programmers a disservice | |
639 | by introducing a hazard for those | |
640 | trying to port code to other compilers which lack any such feature. | |
641 | . | |
b64eb60f MW |
642 | .SH "SEE ALSO" |
643 | .BR mLib (3), | |
644 | .BR macros (3). | |
645 | .PP | |
646 | Simon Tatham, | |
647 | .IR "Metaprogramming custom control structures in C", | |
648 | .BR "https://www.chiark.greenend.org.uk/~sgtatham/mp/" . | |
adec5584 | 649 | . |
b64eb60f MW |
650 | .SH "AUTHOR" |
651 | Mark Wooding, <mdw@distorted.org.uk> |