| 1 | .\" -*-nroff-*- |
| 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 | .. |
| 14 | .TH control 3 "23 April 2023" "Straylight/Edgeware" "mLib utilities library" |
| 15 | . |
| 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 |
| 33 | . |
| 34 | .SH SYNOPSIS |
| 35 | .nf |
| 36 | .B "#include <mLib/control.h>" |
| 37 | |
| 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 |
| 41 | .BI MC_FINALLY( tag ", " cleanup ") " body |
| 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] |
| 45 | .BI MC_LOOPBETWEEN( tag ", " setup ", " cond ", " step ") " loop_body " \fR[\fBelse " else_body \fR] |
| 46 | |
| 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 ); |
| 51 | |
| 52 | .BI MC_ACT( stmt ) |
| 53 | .BI MC_LABEL( tag ) |
| 54 | .BI MC_GOTO( tag ) |
| 55 | .fi |
| 56 | . |
| 57 | .SH DESCRIPTION |
| 58 | The header file |
| 59 | .B <mLib/control.h> |
| 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 |
| 63 | .IR "Metaprogramming custom control structures in C", |
| 64 | though these macros differ from Tatham's in a few respects. |
| 65 | . |
| 66 | .SS "Common features" |
| 67 | Each of these macros takes a |
| 68 | .I tag |
| 69 | argument. |
| 70 | A |
| 71 | .I tag |
| 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 |
| 77 | .IR tag . |
| 78 | If you're writing a new prefix action macro |
| 79 | written in terms of these existing actions, |
| 80 | your macro should receive a |
| 81 | .I tag |
| 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 |
| 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 |
| 92 | statement. |
| 93 | This interferes with the way that `free' |
| 94 | .B break |
| 95 | and |
| 96 | .B continue |
| 97 | statements within the |
| 98 | .I body |
| 99 | behave: |
| 100 | we say that these statements are |
| 101 | .I captured |
| 102 | by the macro. |
| 103 | A |
| 104 | .B break |
| 105 | or |
| 106 | .B continue |
| 107 | statement is |
| 108 | .I free |
| 109 | if it doesn't appear lexically within a loop or |
| 110 | (for |
| 111 | .B break) |
| 112 | .B switch |
| 113 | statement that is part of the |
| 114 | .IR body . |
| 115 | So |
| 116 | .VS |
| 117 | if (!x) break; |
| 118 | .VE |
| 119 | contains a free |
| 120 | .B break |
| 121 | statement, |
| 122 | while |
| 123 | .VS |
| 124 | .ta 2n |
| 125 | for (i = 0; i < n; i++) |
| 126 | if (interestingp(i)) break; |
| 127 | .VE |
| 128 | does not. |
| 129 | .PP |
| 130 | Some of these macros take special care |
| 131 | to give you control over what happens when a captured |
| 132 | .B break |
| 133 | is executed. |
| 134 | Alas, proper handling of |
| 135 | .B continue |
| 136 | doesn't seem possible. |
| 137 | Free |
| 138 | .B break |
| 139 | and |
| 140 | .B continue |
| 141 | statements |
| 142 | .I within |
| 143 | arguments to these macros are never captured. |
| 144 | . |
| 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 , |
| 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 |
| 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 . |
| 177 | Again, the whole thing is syntactically a single statement. |
| 178 | However, |
| 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 |
| 214 | is not executed. |
| 215 | Currently, if the |
| 216 | .I body |
| 217 | executes a free |
| 218 | .B continue |
| 219 | statement, |
| 220 | then control abruptly continues with the |
| 221 | .I onend |
| 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 |
| 228 | .PP |
| 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. |
| 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 |
| 273 | statements are captured. |
| 274 | Currently, a free |
| 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 |
| 292 | statement can introduce a declaration |
| 293 | into its body's scope in C99 and C++; |
| 294 | the macro is not available in C89. |
| 295 | .PP |
| 296 | Executing |
| 297 | .IP |
| 298 | .nf |
| 299 | .ta 2n |
| 300 | .BI MC_LOOPELSE( head ", " tag ") " |
| 301 | .I " loop_body" |
| 302 | .RB [ else |
| 303 | .IR " else_body" ] |
| 304 | .fi |
| 305 | .PP |
| 306 | results in Python-like loop behaviour. |
| 307 | The |
| 308 | .I head |
| 309 | must be a valid loop head with one of the forms |
| 310 | .IP |
| 311 | .nf |
| 312 | .BI "while (" cond ")" |
| 313 | .BI "for (" decl "; " cond "; " next_expr ")" |
| 314 | .BI "MC_DOWHILE(" tag ", " cond ")" |
| 315 | .fi |
| 316 | .PP |
| 317 | The resulting loop executes the same as |
| 318 | .IP |
| 319 | .nf |
| 320 | .ta 2n |
| 321 | .I head |
| 322 | .I " loop_body" |
| 323 | .fi |
| 324 | .PP |
| 325 | If the loop ends abruptly, as a result of |
| 326 | .BR break , |
| 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 |
| 331 | .B else |
| 332 | clause is present, |
| 333 | then the |
| 334 | .I else_body |
| 335 | is executed. |
| 336 | A free |
| 337 | .B continue |
| 338 | statement within the |
| 339 | .I loop_body |
| 340 | behaves normally. |
| 341 | Free |
| 342 | .B break |
| 343 | and |
| 344 | .B continue |
| 345 | statements within the |
| 346 | .I else_body |
| 347 | are not captured. |
| 348 | .PP |
| 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 | . |
| 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 , |
| 417 | with control continuing with the following statement, |
| 418 | skipping the |
| 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 |
| 428 | This is most commonly useful in loops |
| 429 | in order to arrange the correct behaviour of a free |
| 430 | .B break |
| 431 | within the loop body. |
| 432 | See the example below, |
| 433 | which shows the definition |
| 434 | of |
| 435 | .BR MC_LOOPELSE . |
| 436 | .PP |
| 437 | Executing |
| 438 | .IP |
| 439 | .nf |
| 440 | .ta 2n |
| 441 | .BI MC_ALLOWELSE( tag ") " |
| 442 | .I " main_body" |
| 443 | .RB [ else |
| 444 | .IR " else_body" ] |
| 445 | .fi |
| 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 |
| 455 | (if present); |
| 456 | control then naturally transfers to the following statement as usual. |
| 457 | Free |
| 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 |
| 477 | statement: |
| 478 | if |
| 479 | .I main_body |
| 480 | lacks an |
| 481 | .B else |
| 482 | clause, |
| 483 | then an |
| 484 | .B else |
| 485 | intended to match |
| 486 | .B MC_ALLOWELSE |
| 487 | will be mis-associated; |
| 488 | and even if |
| 489 | .I main_body |
| 490 | .I does |
| 491 | have an |
| 492 | .B else |
| 493 | clause, |
| 494 | the resulting program text is likely to provoke a compiler warning |
| 495 | about `dangling |
| 496 | .BR else '. |
| 497 | .PP |
| 498 | Using these tools, |
| 499 | it's relatively straightforward to define a macro like |
| 500 | .BR MC_LOOPELSE , |
| 501 | described above: |
| 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 |
| 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 |
| 524 | statement. |
| 525 | Executing |
| 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 |
| 533 | is usually ignored. |
| 534 | Note that |
| 535 | .B ; |
| 536 | is a valid statement which does nothing, |
| 537 | so |
| 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 |
| 560 | is syntactically an action, |
| 561 | i.e., it's wrapped in |
| 562 | .BR MC_ACT . |
| 563 | The |
| 564 | .IR tag s |
| 565 | here are scoped to the top-level source line, |
| 566 | like all |
| 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 |
| 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.) |
| 592 | The |
| 593 | .I body |
| 594 | appears at the end, |
| 595 | labelled as |
| 596 | .IB tag __body \fR. |
| 597 | Control enters at the start, |
| 598 | and is immediately transferred to the |
| 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 |
| 606 | completes, the loop restarts, |
| 607 | transferring control to |
| 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 |
| 615 | completes, |
| 616 | control transfers to the following statement. |
| 617 | . |
| 618 | .SH "BUGS" |
| 619 | Some macros cause free |
| 620 | .B break |
| 621 | and/or |
| 622 | .B continue |
| 623 | statements to behave in unexpected ways. |
| 624 | .PP |
| 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 |
| 636 | .B __COUNTER__ |
| 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 | . |
| 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/" . |
| 649 | . |
| 650 | .SH "AUTHOR" |
| 651 | Mark Wooding, <mdw@distorted.org.uk> |