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