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