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