diff options
Diffstat (limited to '')
-rw-r--r-- | lib/mp.h | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/lib/mp.h b/lib/mp.h new file mode 100644 index 0000000..df31eae --- /dev/null +++ b/lib/mp.h @@ -0,0 +1,483 @@ +/* + * mp.h: header file providing macros for 'metaprogramming' custom + * loop constructions in standard C. + * + * Accompanies the article on the web at + * https://www.chiark.greenend.org.uk/~sgtatham/mp/ + */ + +/* + * mp.h is copyright 2012 Simon Tatham. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * $Id$ + */ + +/* + * Macros beginning with 'MPI_' are internal to this header file, and + * intended only to be used by other macros defined _in_ this header + * file. Do not refer to them externally. + */ + +/* Standard trickery to allow us to macro-expand and then token-paste */ +#define MPI_TOKPASTEINNER(x,y) x ## y +#define MPI_TOKPASTE(x,y) MPI_TOKPASTEINNER(x,y) + +/* Method of constructing line-unique labels */ +#define MPI_LABEL(id1,id2) \ + MPI_TOKPASTE(MPI_LABEL_ ## id1 ## _ ## id2 ## _, __LINE__) + +/* + * Macros beginning with 'MPP_' and 'MPS_' are building blocks + * intended for metaprogrammers to make useful control constructions + * from. + * + * The prefixes distinguish their syntactic role. MPP_ macros are + * statement _prefixes_; you would typically build a custom control + * structure by defining a macro expanding to a sequence of them. MPS_ + * macros are actual statements, which you might use in the various + * parameters of MPP_ macros that are expected to be statement-shaped. + */ + +/* + * Safety considerations: + * + * - All of these macros are C89-safe, except for MPP_DECLARE if you + * pass an actual declaration and not just an assignment, since + * that one relies on the C99 (and C++) extension of being able to + * write a declaration in the initialisation clause of a for + * statement. + * + * - None of these macros uses switch, so case labels from a switch + * outside the whole lot may be written inside the suffixed + * statement/block. + * + * - All of these constructions use 'goto' with labels constructed + * programmatically, using __LINE__ to make them unique between + * multiple invocations of the same loop macro. So don't put two + * loop macros defined using these building blocks on the same + * source line. + * + * - All of these constructions can be prefixed to something that is + * syntactically a single C statement, and generate something that + * is also a single C statement. So they're if-else safe - you can + * use an unbraced one of these constructs followed by an unbraced + * statement within the then-clause of an outer if, and the else + * will still bind to what it looks as if it ought to. + * + * - Controlling what happens if the user writes a 'break' in the + * suffixed statement is unavoidably rather fiddly. The macros + * below fall into a few categories: + * + * + naturally transparent to 'break' (MPP_BEFORE, MPP_IF). Macros + * of this type will not affect the semantics of 'break' in the + * suffixed statement at all - it will terminate the next + * innermost loop or switch outside the construction. + * + * + artificially transparent to 'break', by means of deliberately + * catching and 'rethrowing' it. (MPP_BREAK_{THROW,CATCH}; + * MPP_BREAK_HANDLER; MPP_FINALLY.) These macros will propagate + * a break outwards to the next containing loop, but in order to + * do so they require that there _be_ a next containing loop, + * since their expansion can't avoid including a break statement + * which they themselves do not wrap in a loop. So you should + * only use these when you know there is a containing loop (e.g. + * because MPP_WHILE or MPP_DO_WHILE precedes them in your + * construction). + * + * + loop constructions. (MPP_WHILE and MPP_DO_WHILE). These + * macros give 'break' the obvious semantics of terminating the + * loop they define. + * + * + break-unsafe macros, which have to include a C looping + * construction to do something not essentially loopy, and hence + * have the unfortunate side effect of causing 'break' to only + * terminate the suffixed statement itself. On the other hand, + * they can be used in contexts where there is no surrounding + * loop at all (which is why I don't just fix them to contain a + * built-in MPP_BREAK_{THROW,CATCH}). + * + * If you are using these macros to build a looping construct, then + * you will probably include an MPP_WHILE or MPP_DO_WHILE in your + * stack, and you'll want 'break' to terminate that. So you just + * need to be sure that break is correctly propagated from the + * suffixed statement back to that loop, which you can do by + * sticking to the break-transparent macros where possible and + * using MPP_BREAK_{THROW,CATCH} to bypass any break-unsafe macro + * such as MPP_DECLARE that you might need to use. Having done + * that, 'break' will do what the user expects. + * + * But if you're using the macros to wrap some context around a + * statement you still intend to be executed only once, there will + * be unavoidable side effects on 'break': you can't use the + * artificially break-unsafe macros because the user might use your + * construction in a context with no surrounding loop at all, so + * you must stick to the naturally break-transparent and the + * break-unsafe, and there aren't enough of the former to be really + * useful. So you must just live with 'break' acquiring unhelpful + * behaviour inside such a macro. + * + * - Almost none of these macros is transparent to 'continue'. The + * naturally break-transparent MPP_BEFORE is, but none of the rest + * can possibly be, because as soon as you include any loop + * construction in the stuff being prefixed to a statement, you + * introduce the invariant that 'continue' is equivalent to jumping + * to the end of the suffixed statement or block. This is not too + * bad if you're defining a custom loop construction (it was quite + * likely the behaviour you wanted for continue anyway), but if you + * were trying to use MPP_DECLARE and/or MPP_BEFORE_AND_AFTER to + * wrap a statement in some context but still only execute it once, + * you'd have to be aware of that limitation. + * + * - MPP_FINALLY and MPP_BREAK_HANDLER can only catch non-local exits + * from the block _by break_. They are not true C++ try/finally, so + * they can't catch other kinds of exit such as return, goto, + * longjmp or exit. + * + * - Finally, it almost goes without saying, but don't forget that + * snippets of code you use as parameters to these macros must + * avoid using commas not contained inside parentheses, or else the + * C preprocessor will consider the comma to end that macro + * parameter and start the next one. If there is any reason you + * really need an unbracketed comma, you can work around this by + * one of two methods: + * - define a macro that expands to a comma ('#define COMMA ,') + * and then use that macro in place of commas in your macro + * argument. It won't be expanded to an actual comma until after + * the argument-separation has finished. + * - if you're allowed to use C99, define a variadic macro that + * expands to its unmodified input argument list ('#define + * WRAP(...) __VA_ARGS__') and then enclose comma-using code in + * WRAP(). Again, this will protect the commas for just long + * enough. + */ + +/* + * MPP_BEFORE: run the code given in the argument 'before' and then + * the suffixed statement. + * + * 'before' should have the syntactic form of one or more declarations + * and statements, except that a trailing semicolon may be omitted. + * Any declarations will be in scope only within 'before', not within + * the suffixed statement. + * + * This macro, unusually among the collection, is naturally + * transparent to 'break' and also transparent to 'continue'. + */ +#define MPP_BEFORE(labid,before) \ + if (1) { \ + before; \ + goto MPI_LABEL(labid, body); \ + } else \ + MPI_LABEL(labid, body): + +/* + * MPP_AFTER: run the suffixed statement, and then the code given in + * the argument 'after'. + * + * 'after' should have the syntactic form of one or more declarations + * and statements, except that a trailing semicolon may be omitted. + * Any declaration in 'after' will be in scope only within 'after'. + * + * This macro is break-unsafe - it causes a 'break' to terminate the + * suffixed statement only. If you need different behaviour, you can + * use MPP_BREAK_CATCH and MPP_BREAK_THROW to pass a break past it - + * but beware that in that case the 'after' clause will not be + * executed, so MPP_FINALLY or MPP_BREAK_HANDLER may be useful too. + */ +#define MPP_AFTER(labid,after) \ + if (1) \ + goto MPI_LABEL(labid, body); \ + else \ + while (1) \ + if (1) { \ + after; \ + break; \ + } else \ + MPI_LABEL(labid, body): + +/* + * MPP_DECLARE: run the 'declaration' argument before the suffixed + * statement. The argument may have the form of either a C expression + * (e.g. an assignment) or a declaration; if the latter, it will be in + * scope within the suffixed statement. + * + * This macro is break-unsafe - it causes a 'break' to terminate the + * suffixed statement only. If you need different behaviour, you can + * use MPP_BREAK_CATCH and MPP_BREAK_THROW to pass a break past it. + */ +#define MPP_DECLARE(labid, declaration) \ + if (0) \ + ; \ + else \ + for (declaration;;) \ + if (1) { \ + goto MPI_LABEL(labid, body); \ + MPI_LABEL(labid, done): break; \ + } else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, done); \ + else \ + MPI_LABEL(labid, body): +/* (The 'if(0) ; else' at the start of the above is just in case we + * encounter an old-style compiler that considers variables declared + * in for statements to have scope extending beyond the for statement. + * Putting another layer outside the 'for' ensures that the variable's + * scope is constrained to _that_ layer even if not to the for itself, + * and it doesn't leak into the calling scope. */ + +/* + * MPP_WHILE: run the suffixed statement within a 'while (condition)' + * loop. + * + * In fact, just writing 'while (condition)' works fine for this, but + * it's nice to make it look like the rest of these macros! + * + * This macro defines an actual loop, and 'break' in the suffixed + * statement terminates that loop as you would expect. + */ +#define MPP_WHILE(labid, condition) \ + while (condition) + +/* + * MPP_DO_WHILE: run the suffixed statement within a loop with the + * semantics of 'do suffixed-statement while (condition)'. + * + * This macro defines an actual loop, and 'break' in the suffixed + * statement terminates that loop as you would expect. + */ +#define MPP_DO_WHILE(labid, condition) \ + if (1) \ + goto MPI_LABEL(labid, body); \ + else \ + while (condition) \ + MPI_LABEL(labid, body): + +/* + * MPP_IF: run the suffixed statement only if 'condition' is true. + * + * This macro is naturally transparent to 'break' and also transparent + * to 'continue'. + */ +#define MPP_IF(labid, condition) \ + if (!(condition)) \ + ; \ + else + +/* + * MPP_BREAK_THROW and MPP_BREAK_CATCH: propagate 'break' control flow + * transfers past other prefixes that mess about with them. + * + * Write an MPP_BREAK_CATCH, then other metaprogramming prefixes from + * this collection, and then an MPP_BREAK_THROW with the same label + * id. If the statement following the MPP_BREAK_THROW terminates by + * 'break', then the effect will be as if the MPP_BREAK_CATCH had + * terminated by 'break', regardless of how the in-between prefixes + * would have handled a 'break'. + * + * These macros are artificially transparent to 'break': they pass + * break through, but include a 'break' statement at the top level of + * MPP_BREAK_CATCH, so that must always be contained inside some loop + * or switch construction. + * + * We also provide MPS_BREAK_THROW, which is a statement-type macro + * that manufactures a break event and passes it to a specified + * MPP_BREAK_CATCH. + */ +#define MPP_BREAK_CATCH(labid) \ + if (0) \ + MPI_LABEL(labid, catch): break; \ + else + +#define MPP_BREAK_THROW(labid) \ + if (1) { \ + goto MPI_LABEL(labid, body); \ + MPI_LABEL(labid, finish):; \ + } else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, catch); \ + else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, finish); \ + else \ + MPI_LABEL(labid, body): + +#define MPS_BREAK_THROW(labid) goto MPI_LABEL(labid, catch) + +/* + * MPP_BREAK_HANDLER: handle a 'break' in the suffixed statement by + * executing the provided handler code and then terminating as if by + * break. + * + * 'handler' should have the syntactic form of one or more + * declarations and statements, except that a trailing semicolon may + * be omitted. + * + * This macro is artificially transparent to 'break': it passes break + * through, but includes a 'break' statement at the top level, so it + * must always be contained inside some loop or switch construction. + */ +#define MPP_BREAK_HANDLER(labid, handler) \ + if (1) { \ + goto MPI_LABEL(labid, body); \ + MPI_LABEL(labid, break): \ + {handler;} \ + break; \ + MPI_LABEL(labid, finish):; \ + } else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, break); \ + else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, finish); \ + else \ + MPI_LABEL(labid, body): + +/* + * MPP_FINALLY: execute the suffixed statement, and execute the + * provided 'finally' clause after it finishes. If it terminates by + * 'break', execute the same 'finally' clause but propagate the break + * to the containing statement. + * + * 'finally' should have the syntactic form of one or more + * declarations and statements, except that a trailing semicolon may + * be omitted. + * + * The 'finally' argument will be double-expanded. Of course it'll + * only be executed once in any given run, so that's not a concern for + * function side effects, but don't do anything fiddly like declaring + * a static variable to which you return a pointer and then expecting + * the pointer to be the same no matter which copy of 'finally' it + * came from. + * + * This macro is artificially transparent to 'break': it passes break + * through, but includes a 'break' statement at the top level, so it + * must always be contained inside some loop or switch construction. + */ +#define MPP_FINALLY(labid, finally) \ + if (1) { \ + goto MPI_LABEL(labid, body); \ + MPI_LABEL(labid, break): \ + {finally;} \ + break; \ + MPI_LABEL(labid, finish): \ + {finally;} \ + } else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, break); \ + else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, finish); \ + else \ + MPI_LABEL(labid, body): + +/* + * MPP_BREAK_STOP: handle a 'break' in the suffixed statement by + * executing the provided handler code and then terminating as if + * normally. + * + * 'handler' should have the syntactic form of one or more + * declarations and statements, except that a trailing semicolon may + * be omitted. + */ +#define MPP_BREAK_STOP(labid, handler) \ + if (1) { \ + goto MPI_LABEL(labid, body); \ + MPI_LABEL(labid, break): \ + {handler;} \ + MPI_LABEL(labid, finish):; \ + } else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, break); \ + else \ + while (1) \ + if (1) \ + goto MPI_LABEL(labid, finish); \ + else \ + MPI_LABEL(labid, body): + +/* + * MPP_ELSE_ACCEPT, MPS_MAIN_INVOKE, MPS_ELSE_INVOKE: arrange to + * accept an optional 'else' clause after the suffixed statement, and + * provide two statement macros which jump to the main clause and the + * else clause. The main (non-else) clause will be be executed in the + * default case, and can be invoked again using MPS_MAIN_INVOKE; + * MPS_ELSE_INVOKE will invoke the else clause. + * + * Like MPP_BREAK_THROW and MPP_BREAK_CATCH, these macros should be + * used in groups with the same label id, so as to match them up to + * each other. MPS_ELSE_INVOKE and MPS_MAIN_INVOKE will go to the + * appropriate clauses corresponding to the MPP_ELSE_ACCEPT with the + * same id. + */ +#define MPP_ELSE_ACCEPT(labid) \ + if (1) \ + goto MPI_LABEL(labid, body); \ + else \ + MPI_LABEL(labid, else): \ + if (0) \ + MPI_LABEL(labid, body): + +#define MPS_MAIN_INVOKE(labid) \ + goto MPI_LABEL(labid, body) + +#define MPS_ELSE_INVOKE(labid) \ + goto MPI_LABEL(labid, else) + +/* + * MPP_ELSE_GENERAL: like MPP_ELSE_ACCEPT, but also lets you provide a + * snippet of code that will be run after the else clause terminates + * and one which will be run after the else clause breaks. + * + * You can use MPS_MAIN_INVOKE and MPS_ELSE_INVOKE with this as well + * as with MPP_ELSE_ACCEPT. + * + * Will mess up what happens after the main body, so you'll probably + * want to follow this macro with others such as MPP_AFTER and + * something to catch break in the main body too. + */ +#define MPP_ELSE_GENERAL(labid, after, breakhandler) \ + if (1) \ + goto MPI_LABEL(labid, body); \ + else \ + while (1) \ + if (1) { \ + {breakhandler;} \ + break; \ + } else \ + while (1) \ + if (1) { \ + {after;} \ + break; \ + } else \ + MPI_LABEL(labid, else): \ + if (0) \ + MPI_LABEL(labid, body): |