├── LICENSE.md ├── Makefile ├── README.md ├── generate.sh ├── include └── macro_sequence_for.h ├── macro_sequence_for.codegen_input.h └── tests.cpp /LICENSE.md: -------------------------------------------------------------------------------- 1 | BSD Zero Clause License 2 | 3 | Copyright (c) 2022 Egor Mikhailov 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 14 | PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # This makefile runs tests. It also runs `generate.sh` automatically, if the generated header is out of date. 2 | 3 | override comma := , 4 | 5 | # A space-separated list of compilers. 6 | # Normally those are detected automatically based on `COMPILER_PATTERNS`, but you can override them. 7 | COMPILER := $(strip $(shell bash -c "compgen -c 'g++-'; compgen -c 'clang++'" | grep -Po '^((clan)?g\+\+(-[0-9]+)?|cl|clang-cl)(?=.exe)?$$' | sort -hr -t- -k2 | uniq)) 8 | # Adjust the compiler list to test the `-fms-{compatibility,extensions}` flags too. 9 | COMPILER := $(foreach x,$(COMPILER),$(if $(filter g++%,$x),$x $x$(comma)-fms-extensions,$(if $(filter clang++%,$x),$x $x$(comma)-fms-compatibility,$x))) 10 | 11 | # The source file used for code generation. 12 | INPUT=macro_sequence_for.codegen_input.h 13 | # The result of the generation. 14 | OUTPUT=include/macro_sequence_for.h 15 | 16 | .PHONY: tests 17 | tests: tests.cpp $(OUTPUT) 18 | @true $(foreach x,$(COMPILER),&& echo -n "$(subst $(comma), ,$x) " && $(subst $(comma), ,$x) -std=c++14 -Wall -Wextra -pedantic-errors -Iinclude $< -c -o $(shell mktemp) && echo "- OK") 19 | 20 | $(OUTPUT): $(INPUT) generate.sh 21 | @mkdir -p "$(dir $(OUTPUT))" 22 | ./generate.sh 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## macro_sequence_for.h 2 | 3 | A 'for each' loop macro, with unlimited number of iterations (not limited by the amount of generated boilerplate macros), and with the ability to transfer state between iterations. 4 | 5 | * [The motivating example](#the-motivating-example) 6 | * [Common function arguments](#common-function-arguments) 7 | * [Recursion](#recursion) 8 | * [How?](#how) 9 | * [Unlimited number of iterations?](#unlimited-number-of-iterations) 10 | * [Ability to transfer state between iterations?](#ability-to-transfer-state-between-iterations) 11 | 12 | ### The motivating example 13 | ```cpp 14 | #include "macro_sequence_for.h" 15 | 16 | #define MAKE_FLAGS(name, seq) enum name {SF_FOR_EACH(BODY, STEP, FINAL, 0, seq)}; 17 | #define BODY(n, d, x) x = 1 << (d), 18 | #define STEP(n, d, x) d+1 19 | #define FINAL(n, d) _mask = (1 << (d)) - 1 20 | 21 | MAKE_FLAGS(E, (a)(b)(c)) 22 | ``` 23 | This expands to: 24 | ```cpp 25 | enum E 26 | { 27 | a = 1 << (0), // 0b0001 28 | b = 1 << (0+1), // 0b0010 29 | c = 1 << (0+1+1), // 0b0100 30 | _mask = (1 << (0+1+1+1)) - 1 // 0b0111 31 | }; 32 | ``` 33 | Here, `BODY()` is executed for every element of `(a)(b)(c)` to generate the respective enum constant. Each `BODY` invocation receives one element of the input sequence (`x`) and the "state" (`d`, which acts as a counter in this case). 34 | 35 | The initial value of the state is taken from the fourth argument of `SF_FOR_EACH()`, and then the state is updated by calling `STEP()` on every iteration (in this case, it increments the counter). 36 | 37 | Lastly, `FINAL()` is called after the loop ends, receiving the final state (here, the value of the counter after the final increment). 38 | 39 | In all three macros, `n` receives the current loop nesting level (`0` in the case, since the loop is flat). 40 | 41 | ### Common function arguments 42 | 43 | We provide a few simple functions as predefined arguments for `SF_FOR_EACH`: 44 | 45 | * `SF_NULL`, which expands to nothing, and 46 | * `SF_STATE`, which expands to its second argument. 47 | 48 | Example: Computing a sum. 49 | ```cpp 50 | #define SUM(seq) SF_FOR_EACH(SF_NULL, SUM_STEP, SF_STATE, 0, seq) 51 | #define SUM_STEP(n, d, x) d+x 52 | int x = SUM((1)(2)(3)); // 0+1+2+3 53 | ``` 54 | 55 | Example: Declaring multiple variables with the same value. 56 | ```cpp 57 | #define DECL(value, seq) SF_FOR_EACH(DECL_BODY, SF_STATE, SF_NULL, value, seq) 58 | #define DECL_BODY(n, d, x) int x = d; 59 | DECL(42, (x)(y)(z)) // int x = 42; int y = 42; int z = 42; 60 | ``` 61 | 62 | ### Recursion 63 | 64 | We do support recursive loops, but the depth is limited by the amount of generated boilerplate macros. The max depth is reported by `SF_MAX_DEPTH`. Edit `generate.sh` and re-run it if you need to change this parameter. 65 | 66 | Since the same macro can't expand recursively, each recursion level has to use a different variation of `SF_FOR_EACH`: suffixed either with nothing or with `0`...`SF_SF_MAX_DEPTH-1`. 67 | 68 | Example: 69 | ```cpp 70 | #define ARRAYS(seq) SF_FOR_EACH(ARRAYS_BODY, SF_NULL, SF_NULL,, seq) 71 | #define ARRAYS_BODY(n, d, name, values) int name[] = {SF_FOR_EACH2(ARRAYS_BODY2, SF_NULL, SF_NULL,, values)}; 72 | #define ARRAYS_BODY2(n, d, x) x, 73 | 74 | ARRAYS((f,(1)(2)(3))(g,(4)(5))) // int f[] = {1,2,3,}; int g[] = {4,5,}; 75 | ``` 76 | 77 | You can automatically get the recursion level from the `n` argument, and call `SF_FOR_EACH(...)` as `SF_CAT(SF_FOR_EACH,n)(...)` to glue the number to the macro name. 78 | 79 | ### Some extras 80 | 81 | `STEP()` can emit output in addition to `BODY()`, by returning `d, ...` (`...` gets emitted as text after the result of `BODY()`, while `d` gets assigned to the state variable). This can let you avoid duplicate work in some cases, and often looks better in general. The downside is that it can't emit unbalanced parentheses. 82 | 83 | ### How? 84 | 85 | The basic idea is taken from the [PPMP Iceberg](https://jadlevesque.github.io/PPMP-Iceberg/explanations#codefxyyyyycode) article: to convert the sequence of the form `(a)(b)(c)` to `a) b) c)`. They call those "guides". 86 | 87 | For some reason, I'm unable to find any existing implementation using this idea. 88 | 89 | The article uses a wonky termination condition (each element has to start with a letter or digit, a specific element was reserved for ending the loop). 90 | 91 | I've fixed this limitation by producing `)a) )b) )c) 0)` instead of `a) b) c)`, where `)...` indicates the next sequence element, and `0)` indicates the end of the loop. 92 | 93 | 94 | #### Unlimited number of iterations? 95 | 96 | Most implementations of the macro loops (e.g. [`BOOST_PP_SEQ_FOR_EACH()`](https://www.boost.org/doc/libs/1_80_0/libs/preprocessor/doc/ref/seq_for_each.html)) have a limit on the number of iterations, determined by the amount of boilerplate macros they've generated. 97 | 98 | Here's a minimal example of such a loop, supporting up to 4 iterations. 99 | ```cpp 100 | #define LOOP(m, ...) __VA_OPT__(LOOP_0(m, __VA_ARGS__)) 101 | #define LOOP_0(m, x, ...) m(x) __VA_OPT__(LOOP_1(m, __VA_ARGS__)) 102 | #define LOOP_1(m, x, ...) m(x) __VA_OPT__(LOOP_2(m, __VA_ARGS__)) 103 | #define LOOP_2(m, x, ...) m(x) __VA_OPT__(LOOP_3(m, __VA_ARGS__)) 104 | #define LOOP_3(m, x, ...) m(x) __VA_OPT__(LOOP_4(m, __VA_ARGS__)) 105 | 106 | #define BODY(x) [x] 107 | LOOP(BODY, 1, 2, 3) // Ok: [1][2][3] 108 | LOOP(BODY, 1, 2, 3, 4) // Ok: [1][2][3][4] 109 | LOOP(BODY, 1, 2, 3, 4, 5) // Breaks: [1][2][3][4] LOOP_4(BODY, 5) 110 | ``` 111 | 112 | In general, boilerplate-based loops are more flexible, since they're not limited to iterating over sequences, and can perform arbitrary computations (see [`BOOST_PP_FOR()`](https://www.boost.org/doc/libs/1_80_0/libs/preprocessor/doc/ref/for.html)). But they seem to be inferior for the 'for each' loops. 113 | 114 | Our macro doesn't have a limit on the number of iterations, it's not constrained by the amount of boilerplate. 115 | 116 | But we still have *some* boilerplate to support nested loops, and the nesting level *is* limited by the amount of boilerplate. 117 | 118 | #### Ability to transfer state between iterations? 119 | 120 | This refers to the ability to forward the parameter of `SF_FOR_EACH()` to the loop body (`BODY()` in the example above), and to modify it between iterations. 121 | 122 | While this seems like an obvious feature to have, certain kinds of simple loops don't have it. 123 | 124 | Consider the following boilerplate-less loop, which doesn't have a limit on the number of iterations: 125 | ```cpp 126 | #define LOOP(seq) END(A seq) 127 | #define BODY(x) int x; 128 | #define A(x) BODY(x) B 129 | #define B(x) BODY(x) A 130 | #define A_END 131 | #define B_END 132 | #define END(...) END_(__VA_ARGS__) 133 | #define END_(...) __VA_ARGS__##_END 134 | 135 | LOOP((a)(b)(c)) // int a; int b; int c; 136 | ``` 137 | 138 | What's not to like here? 139 | 140 | Firstly, we can't forward any state from outside to the loop body. It's impossible to implement `LOOP(42, (a)(b)(c))`->`int a=42; int b=42; int c=42;` with this approach. 141 | 142 | Similarly, this doesn't let us preserve state between iterations. E.g. we can't produce `int a=1; int b=1+1; int c=1+1+1;`. 143 | 144 | And lastly, we can't abstract away this loop into a single macro, since passing the `BODY()` into such macro would too constitute "forwarding state into the loop body". 145 | -------------------------------------------------------------------------------- /generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Generates the header file, adding the appropriate boilerplate. 4 | # See `macro_sequence_for.codegen_input.h` for the details on what is being generated. 5 | 6 | # The max level of loop nesting. (The number of iterations is always unlimited.) 7 | N=8 8 | 9 | INPUT=macro_sequence_for.codegen_input.h 10 | OUTPUT=include/macro_sequence_for.h 11 | 12 | mkdir -p "$(dirname "$OUTPUT")" 13 | 14 | # The base part, aka the -1th nesting level. 15 | # `-e '$d'` deletes the last line, which is the `#endif` of the include guard. 16 | sed -e '$d' -e 's/xx//g' -e 's/yy/0/g' -e 's/SF_MAX_DEPTH 0/SF_MAX_DEPTH '"$N"'/' "$INPUT" | grep -vE '^//\?\?' >"$OUTPUT" 17 | 18 | # Boilerplate for nesting levels `0..N-1`. 19 | for ((i=0; $i<$N; i=$(($i+1)))) 20 | do 21 | echo "// $i" >>"$OUTPUT" 22 | grep '#define \w*xx\w*' "$INPUT" | sed -e 's/xx/'"$i"'/g' -e 's/yy/'"$((i+1))"'/g' >>"$OUTPUT" 23 | done 24 | 25 | # Lastly, close the include guard. 26 | echo $'\n#endif' >>"$OUTPUT" 27 | -------------------------------------------------------------------------------- /include/macro_sequence_for.h: -------------------------------------------------------------------------------- 1 | #ifndef MACRO_SEQUENCE_FOR_H_ // Intentionally not a `#pragma once`, to be able to tolerate multiple copies of the file. 2 | #define MACRO_SEQUENCE_FOR_H_ 3 | 4 | // "macro_sequence_for.h", v0.3 5 | // Implements macro loops with unlimited number of iterations, over sequences of the form `(a)(b)(c)` (though the nesting level is limited). 6 | // See `SF_FOR_EACH()` below for the usage explanation. 7 | 8 | // BSD Zero Clause License 9 | // 10 | // Copyright (c) 2024 Egor Mikhailov 11 | // 12 | // Permission to use, copy, modify, and/or distribute this software for any 13 | // purpose with or without fee is hereby granted. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 16 | // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 17 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 18 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 19 | // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 20 | // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 21 | // PERFORMANCE OF THIS SOFTWARE. 22 | 23 | 24 | // A loop over a sequence of the form `(a)(b)(c)`. The elements can contain commas. 25 | // `m` is the loop body, invoked as `m(n, d, ...)`, where `...` is one of the elements, 26 | // `d` initially matches the argument `d` of `SF_FOR_EACH`, but is replaced with 27 | // the result of `s(n, d, ...)` after every iteration. 28 | // After the loop finishes, `f(n, d)` is called with the final value of `d`. 29 | 30 | // Additionally, if `s` returns something with a `,`, everything after the first comma 31 | // gets pasted to the output after `m(...)` on the same iteration. This is sometimes 32 | // useful in complex macros, where you'd otherwise repeat the same computation 33 | // in both `m` and `s`. 34 | // But note that while `m` can return absolutely anything, `s` can't return mismatched parentheses. 35 | // 36 | // The `n` argument receives the next available loop nesting level. `SF_FOR_EACH` 37 | // sets it to `0`, while `SF_FOR_EACHi` sets it to `i+1`. 38 | // Use `SF_FOR_EACHi(...)` for nested loops, where `i` can either be hardcoded, 39 | // or come from the `n` argument of the outer loop, in which case you 40 | // can use `SF_CAT(SF_FOR_EACH, n)(...)`. 41 | #define SF_FOR_EACH(m, s, f, d, seq) IMPL_SEQFOR_FOR(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 42 | 43 | // Various useful macros, to be passed as arguments to `SF_FOR_EACH`. 44 | #define SF_NULL(...) // A no-op. 45 | #define SF_STATE(...) IMPL_SEQFOR_STATE(__VA_ARGS__,) // Returns the second argument. As the step and/or final function, returns the state unchanged. 46 | 47 | // Concatenate two identifiers. 48 | #define SF_CAT(x, y) SF_CAT_(x, y) 49 | #define SF_CAT_(x, y) x##y 50 | 51 | // The max loop nesting level. This is baked in at the code generation time, adjust it in `generate.sh`. 52 | // The actual number of supporting nesting levels is `N+1`, with name suffixes ``, `0`, `1`, ..., `N-1`. 53 | // The most nested loop will report `N` as the next available level. 54 | #define SF_MAX_DEPTH 8 55 | 56 | 57 | // Implementation: 58 | 59 | #if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL == 1) 60 | #error The standard-conformant MSVC preprocessor is required, enable it with `/Zc:preprocessor`. 61 | #endif 62 | 63 | // Same as `SF_FOR_EACH`, but the list is spelled differently, as described in `IMPL_SEQFOR_FOR_GUIDE_A`. 64 | #define IMPL_SEQFOR_FOR(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY( IMPL_SEQFOR_FOR_GUIDE_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 65 | 66 | // Various helpers. 67 | #define IMPL_SEQFOR_NULL(...) 68 | #define IMPL_SEQFOR_IDENTITY(...) __VA_ARGS__ 69 | #define IMPL_SEQFOR_LPAREN ( 70 | #define IMPL_SEQFOR_CAT(x, y) IMPL_SEQFOR_CAT_(x,y) 71 | #define IMPL_SEQFOR_CAT_(x, y) x##y 72 | // Implementation of `SF_STATE`. 73 | #define IMPL_SEQFOR_STATE(n, d, ...) d 74 | 75 | // `IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)` converts `seq` from e.g. `(a)(b)(c)` to `()(a)()(b)()(c)(0)`. 76 | #define IMPL_SEQFOR_ANNOTATE_SEQ_A(...) ()(__VA_ARGS__)IMPL_SEQFOR_ANNOTATE_SEQ_B 77 | #define IMPL_SEQFOR_ANNOTATE_SEQ_B(...) ()(__VA_ARGS__)IMPL_SEQFOR_ANNOTATE_SEQ_A 78 | #define IMPL_SEQFOR_ANNOTATE_SEQ_A_END 79 | #define IMPL_SEQFOR_ANNOTATE_SEQ_B_END 80 | #define IMPL_SEQFOR_ANNOTATE_SEQ_END(x) IMPL_SEQFOR_ANNOTATE_SEQ_END_(x)(0) 81 | #define IMPL_SEQFOR_ANNOTATE_SEQ_END_(x) x##_END 82 | 83 | // `IMPL_SEQFOR_TO_GUIDE_A seq` converts `seq` from e.g. `(a)(b)(c)` to `a)b)c)IMPL_SEQFOR_TO_GUIDE_A` (or ...`_B`). 84 | // We call the resulting sequence `a)b)c)` a "guide". 85 | #define IMPL_SEQFOR_TO_GUIDE_A(...) __VA_ARGS__)IMPL_SEQFOR_TO_GUIDE_B 86 | #define IMPL_SEQFOR_TO_GUIDE_B(...) __VA_ARGS__)IMPL_SEQFOR_TO_GUIDE_A 87 | 88 | // Usage: `IMPL_SEQFOR_FOR_GUIDE_A(m,s,d,`, followed by a sequence of 0+ blocks of the form `)x)`, followed by `0)`, finally followed by arbitrary junk. 89 | // For each `)x)` in the sequence, expands to `m(r,d,x)`, then modifies `d` to contain `s(r,d,x)`, where `r` is the next available loop nesting level. 90 | #define IMPL_SEQFOR_FOR_GUIDE_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE_A_, e)(m, s, f, d) 91 | #define IMPL_SEQFOR_FOR_GUIDE_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE_B(m, s, f, d, 92 | #define IMPL_SEQFOR_FOR_GUIDE_A_0(m, s, f, d) f(0, d) IMPL_SEQFOR_NULL( 93 | #define IMPL_SEQFOR_FOR_GUIDE_B(m, s, f, d, ...) m(0, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE_B_0(m, s, f, s(0, d, __VA_ARGS__)) 94 | #define IMPL_SEQFOR_FOR_GUIDE_B_0(...) IMPL_SEQFOR_FOR_GUIDE_B_1(__VA_ARGS__) 95 | #define IMPL_SEQFOR_FOR_GUIDE_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE_A(m, s, f, d, 96 | 97 | 98 | // Generated boilerplate for nested loops: 99 | // 0 100 | #define SF_FOR_EACH0(m, s, f, d, seq) IMPL_SEQFOR_FOR0(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 101 | #define IMPL_SEQFOR_FOR0(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY0( IMPL_SEQFOR_FOR_GUIDE0_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 102 | #define IMPL_SEQFOR_IDENTITY0(...) __VA_ARGS__ 103 | #define IMPL_SEQFOR_FOR_GUIDE0_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE0_A_, e)(m, s, f, d) 104 | #define IMPL_SEQFOR_FOR_GUIDE0_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE0_B(m, s, f, d, 105 | #define IMPL_SEQFOR_FOR_GUIDE0_A_0(m, s, f, d) f(1, d) IMPL_SEQFOR_NULL( 106 | #define IMPL_SEQFOR_FOR_GUIDE0_B(m, s, f, d, ...) m(1, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE0_B_0(m, s, f, s(1, d, __VA_ARGS__)) 107 | #define IMPL_SEQFOR_FOR_GUIDE0_B_0(...) IMPL_SEQFOR_FOR_GUIDE0_B_1(__VA_ARGS__) 108 | #define IMPL_SEQFOR_FOR_GUIDE0_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE0_A(m, s, f, d, 109 | // 1 110 | #define SF_FOR_EACH1(m, s, f, d, seq) IMPL_SEQFOR_FOR1(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 111 | #define IMPL_SEQFOR_FOR1(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY1( IMPL_SEQFOR_FOR_GUIDE1_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 112 | #define IMPL_SEQFOR_IDENTITY1(...) __VA_ARGS__ 113 | #define IMPL_SEQFOR_FOR_GUIDE1_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE1_A_, e)(m, s, f, d) 114 | #define IMPL_SEQFOR_FOR_GUIDE1_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE1_B(m, s, f, d, 115 | #define IMPL_SEQFOR_FOR_GUIDE1_A_0(m, s, f, d) f(2, d) IMPL_SEQFOR_NULL( 116 | #define IMPL_SEQFOR_FOR_GUIDE1_B(m, s, f, d, ...) m(2, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE1_B_0(m, s, f, s(2, d, __VA_ARGS__)) 117 | #define IMPL_SEQFOR_FOR_GUIDE1_B_0(...) IMPL_SEQFOR_FOR_GUIDE1_B_1(__VA_ARGS__) 118 | #define IMPL_SEQFOR_FOR_GUIDE1_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE1_A(m, s, f, d, 119 | // 2 120 | #define SF_FOR_EACH2(m, s, f, d, seq) IMPL_SEQFOR_FOR2(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 121 | #define IMPL_SEQFOR_FOR2(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY2( IMPL_SEQFOR_FOR_GUIDE2_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 122 | #define IMPL_SEQFOR_IDENTITY2(...) __VA_ARGS__ 123 | #define IMPL_SEQFOR_FOR_GUIDE2_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE2_A_, e)(m, s, f, d) 124 | #define IMPL_SEQFOR_FOR_GUIDE2_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE2_B(m, s, f, d, 125 | #define IMPL_SEQFOR_FOR_GUIDE2_A_0(m, s, f, d) f(3, d) IMPL_SEQFOR_NULL( 126 | #define IMPL_SEQFOR_FOR_GUIDE2_B(m, s, f, d, ...) m(3, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE2_B_0(m, s, f, s(3, d, __VA_ARGS__)) 127 | #define IMPL_SEQFOR_FOR_GUIDE2_B_0(...) IMPL_SEQFOR_FOR_GUIDE2_B_1(__VA_ARGS__) 128 | #define IMPL_SEQFOR_FOR_GUIDE2_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE2_A(m, s, f, d, 129 | // 3 130 | #define SF_FOR_EACH3(m, s, f, d, seq) IMPL_SEQFOR_FOR3(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 131 | #define IMPL_SEQFOR_FOR3(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY3( IMPL_SEQFOR_FOR_GUIDE3_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 132 | #define IMPL_SEQFOR_IDENTITY3(...) __VA_ARGS__ 133 | #define IMPL_SEQFOR_FOR_GUIDE3_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE3_A_, e)(m, s, f, d) 134 | #define IMPL_SEQFOR_FOR_GUIDE3_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE3_B(m, s, f, d, 135 | #define IMPL_SEQFOR_FOR_GUIDE3_A_0(m, s, f, d) f(4, d) IMPL_SEQFOR_NULL( 136 | #define IMPL_SEQFOR_FOR_GUIDE3_B(m, s, f, d, ...) m(4, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE3_B_0(m, s, f, s(4, d, __VA_ARGS__)) 137 | #define IMPL_SEQFOR_FOR_GUIDE3_B_0(...) IMPL_SEQFOR_FOR_GUIDE3_B_1(__VA_ARGS__) 138 | #define IMPL_SEQFOR_FOR_GUIDE3_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE3_A(m, s, f, d, 139 | // 4 140 | #define SF_FOR_EACH4(m, s, f, d, seq) IMPL_SEQFOR_FOR4(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 141 | #define IMPL_SEQFOR_FOR4(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY4( IMPL_SEQFOR_FOR_GUIDE4_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 142 | #define IMPL_SEQFOR_IDENTITY4(...) __VA_ARGS__ 143 | #define IMPL_SEQFOR_FOR_GUIDE4_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE4_A_, e)(m, s, f, d) 144 | #define IMPL_SEQFOR_FOR_GUIDE4_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE4_B(m, s, f, d, 145 | #define IMPL_SEQFOR_FOR_GUIDE4_A_0(m, s, f, d) f(5, d) IMPL_SEQFOR_NULL( 146 | #define IMPL_SEQFOR_FOR_GUIDE4_B(m, s, f, d, ...) m(5, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE4_B_0(m, s, f, s(5, d, __VA_ARGS__)) 147 | #define IMPL_SEQFOR_FOR_GUIDE4_B_0(...) IMPL_SEQFOR_FOR_GUIDE4_B_1(__VA_ARGS__) 148 | #define IMPL_SEQFOR_FOR_GUIDE4_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE4_A(m, s, f, d, 149 | // 5 150 | #define SF_FOR_EACH5(m, s, f, d, seq) IMPL_SEQFOR_FOR5(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 151 | #define IMPL_SEQFOR_FOR5(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY5( IMPL_SEQFOR_FOR_GUIDE5_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 152 | #define IMPL_SEQFOR_IDENTITY5(...) __VA_ARGS__ 153 | #define IMPL_SEQFOR_FOR_GUIDE5_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE5_A_, e)(m, s, f, d) 154 | #define IMPL_SEQFOR_FOR_GUIDE5_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE5_B(m, s, f, d, 155 | #define IMPL_SEQFOR_FOR_GUIDE5_A_0(m, s, f, d) f(6, d) IMPL_SEQFOR_NULL( 156 | #define IMPL_SEQFOR_FOR_GUIDE5_B(m, s, f, d, ...) m(6, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE5_B_0(m, s, f, s(6, d, __VA_ARGS__)) 157 | #define IMPL_SEQFOR_FOR_GUIDE5_B_0(...) IMPL_SEQFOR_FOR_GUIDE5_B_1(__VA_ARGS__) 158 | #define IMPL_SEQFOR_FOR_GUIDE5_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE5_A(m, s, f, d, 159 | // 6 160 | #define SF_FOR_EACH6(m, s, f, d, seq) IMPL_SEQFOR_FOR6(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 161 | #define IMPL_SEQFOR_FOR6(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY6( IMPL_SEQFOR_FOR_GUIDE6_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 162 | #define IMPL_SEQFOR_IDENTITY6(...) __VA_ARGS__ 163 | #define IMPL_SEQFOR_FOR_GUIDE6_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE6_A_, e)(m, s, f, d) 164 | #define IMPL_SEQFOR_FOR_GUIDE6_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE6_B(m, s, f, d, 165 | #define IMPL_SEQFOR_FOR_GUIDE6_A_0(m, s, f, d) f(7, d) IMPL_SEQFOR_NULL( 166 | #define IMPL_SEQFOR_FOR_GUIDE6_B(m, s, f, d, ...) m(7, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE6_B_0(m, s, f, s(7, d, __VA_ARGS__)) 167 | #define IMPL_SEQFOR_FOR_GUIDE6_B_0(...) IMPL_SEQFOR_FOR_GUIDE6_B_1(__VA_ARGS__) 168 | #define IMPL_SEQFOR_FOR_GUIDE6_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE6_A(m, s, f, d, 169 | // 7 170 | #define SF_FOR_EACH7(m, s, f, d, seq) IMPL_SEQFOR_FOR7(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 171 | #define IMPL_SEQFOR_FOR7(m, s, f, d, guide) IMPL_SEQFOR_IDENTITY7( IMPL_SEQFOR_FOR_GUIDE7_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 172 | #define IMPL_SEQFOR_IDENTITY7(...) __VA_ARGS__ 173 | #define IMPL_SEQFOR_FOR_GUIDE7_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDE7_A_, e)(m, s, f, d) 174 | #define IMPL_SEQFOR_FOR_GUIDE7_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDE7_B(m, s, f, d, 175 | #define IMPL_SEQFOR_FOR_GUIDE7_A_0(m, s, f, d) f(8, d) IMPL_SEQFOR_NULL( 176 | #define IMPL_SEQFOR_FOR_GUIDE7_B(m, s, f, d, ...) m(8, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDE7_B_0(m, s, f, s(8, d, __VA_ARGS__)) 177 | #define IMPL_SEQFOR_FOR_GUIDE7_B_0(...) IMPL_SEQFOR_FOR_GUIDE7_B_1(__VA_ARGS__) 178 | #define IMPL_SEQFOR_FOR_GUIDE7_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDE7_A(m, s, f, d, 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /macro_sequence_for.codegen_input.h: -------------------------------------------------------------------------------- 1 | //?? -- Codegeneration explained -- 2 | //?? This file is used as a source for generating the final header. 3 | //?? While this file is self-sufficient, it doesn't have any boilerplate macros, preventing you from being able to nest loops. 4 | //?? Any macros having `xx` in their names are duplicated for each nesting level, with `xx` being replaced with ``,`0`,`1`, etc. 5 | //?? `yy` is replaced with the next nesting level. 6 | //?? Lastly, any lines beginning with `//??` are removed. 7 | //?? ---- 8 | #ifndef MACRO_SEQUENCE_FOR_H_ // Intentionally not a `#pragma once`, to be able to tolerate multiple copies of the file. 9 | #define MACRO_SEQUENCE_FOR_H_ 10 | 11 | // "macro_sequence_for.h", v0.3 12 | // Implements macro loops with unlimited number of iterations, over sequences of the form `(a)(b)(c)` (though the nesting level is limited). 13 | // See `SF_FOR_EACH()` below for the usage explanation. 14 | 15 | // BSD Zero Clause License 16 | // 17 | // Copyright (c) 2022 Egor Mikhailov 18 | // 19 | // Permission to use, copy, modify, and/or distribute this software for any 20 | // purpose with or without fee is hereby granted. 21 | // 22 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 23 | // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 24 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 25 | // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 26 | // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 27 | // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 28 | // PERFORMANCE OF THIS SOFTWARE. 29 | 30 | 31 | // A loop over a sequence of the form `(a)(b)(c)`. The elements can contain commas. 32 | // `m` is the loop body, invoked as `m(n, d, ...)`, where `...` is one of the elements, 33 | // `d` initially matches the argument `d` of `SF_FOR_EACH`, but is replaced with 34 | // the result of `s(n, d, ...)` after every iteration. 35 | // After the loop finishes, `f(n, d)` is called with the final value of `d`. 36 | 37 | // Additionally, if `s` returns something with a `,`, everything after the first comma 38 | // gets pasted to the output after `m(...)` on the same iteration. This is sometimes 39 | // useful in complex macros, where you'd otherwise repeat the same computation 40 | // in both `m` and `s`. 41 | // But note that while `m` can return absolutely anything, `s` can't return mismatched parentheses. 42 | // 43 | // The `n` argument receives the next available loop nesting level. `SF_FOR_EACH` 44 | // sets it to `0`, while `SF_FOR_EACHi` sets it to `i+1`. 45 | // Use `SF_FOR_EACHi(...)` for nested loops, where `i` can either be hardcoded, 46 | // or come from the `n` argument of the outer loop, in which case you 47 | // can use `SF_CAT(SF_FOR_EACH, n)(...)`. 48 | #define SF_FOR_EACHxx(m, s, f, d, seq) IMPL_SEQFOR_FORxx(m, s, f, d, IMPL_SEQFOR_TO_GUIDE_A IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)) ) 49 | 50 | // Various useful macros, to be passed as arguments to `SF_FOR_EACH`. 51 | #define SF_NULL(...) // A no-op. 52 | #define SF_STATE(...) IMPL_SEQFOR_STATE(__VA_ARGS__,) // Returns the second argument. As the step and/or final function, returns the state unchanged. 53 | 54 | // Concatenate two identifiers. 55 | #define SF_CAT(x, y) SF_CAT_(x, y) 56 | #define SF_CAT_(x, y) x##y 57 | 58 | // The max loop nesting level. This is baked in at the code generation time, adjust it in `generate.sh`. 59 | // The actual number of supporting nesting levels is `N+1`, with name suffixes ``, `0`, `1`, ..., `N-1`. 60 | // The most nested loop will report `N` as the next available level. 61 | #define SF_MAX_DEPTH 0 62 | 63 | 64 | // Implementation: 65 | 66 | #if defined(_MSC_VER) && (!defined(_MSVC_TRADITIONAL) || _MSVC_TRADITIONAL == 1) 67 | #error The standard-conformant MSVC preprocessor is required, enable it with `/Zc:preprocessor`. 68 | #endif 69 | 70 | // Same as `SF_FOR_EACH`, but the list is spelled differently, as described in `IMPL_SEQFOR_FOR_GUIDExx_A`. 71 | #define IMPL_SEQFOR_FORxx(m, s, f, d, guide) IMPL_SEQFOR_IDENTITYxx( IMPL_SEQFOR_FOR_GUIDExx_A IMPL_SEQFOR_LPAREN m, s, f, d, guide ) 72 | 73 | // Various helpers. 74 | #define IMPL_SEQFOR_NULL(...) 75 | #define IMPL_SEQFOR_IDENTITYxx(...) __VA_ARGS__ 76 | #define IMPL_SEQFOR_LPAREN ( 77 | #define IMPL_SEQFOR_CAT(x, y) IMPL_SEQFOR_CAT_(x,y) 78 | #define IMPL_SEQFOR_CAT_(x, y) x##y 79 | // Implementation of `SF_STATE`. 80 | #define IMPL_SEQFOR_STATE(n, d, ...) d 81 | 82 | // `IMPL_SEQFOR_ANNOTATE_SEQ_END(IMPL_SEQFOR_ANNOTATE_SEQ_A seq)` converts `seq` from e.g. `(a)(b)(c)` to `()(a)()(b)()(c)(0)`. 83 | #define IMPL_SEQFOR_ANNOTATE_SEQ_A(...) ()(__VA_ARGS__)IMPL_SEQFOR_ANNOTATE_SEQ_B 84 | #define IMPL_SEQFOR_ANNOTATE_SEQ_B(...) ()(__VA_ARGS__)IMPL_SEQFOR_ANNOTATE_SEQ_A 85 | #define IMPL_SEQFOR_ANNOTATE_SEQ_A_END 86 | #define IMPL_SEQFOR_ANNOTATE_SEQ_B_END 87 | #define IMPL_SEQFOR_ANNOTATE_SEQ_END(x) IMPL_SEQFOR_ANNOTATE_SEQ_END_(x)(0) 88 | #define IMPL_SEQFOR_ANNOTATE_SEQ_END_(x) x##_END 89 | 90 | // `IMPL_SEQFOR_TO_GUIDE_A seq` converts `seq` from e.g. `(a)(b)(c)` to `a)b)c)IMPL_SEQFOR_TO_GUIDE_A` (or ...`_B`). 91 | // We call the resulting sequence `a)b)c)` a "guide". 92 | #define IMPL_SEQFOR_TO_GUIDE_A(...) __VA_ARGS__)IMPL_SEQFOR_TO_GUIDE_B 93 | #define IMPL_SEQFOR_TO_GUIDE_B(...) __VA_ARGS__)IMPL_SEQFOR_TO_GUIDE_A 94 | 95 | // Usage: `IMPL_SEQFOR_FOR_GUIDExx_A(m,s,d,`, followed by a sequence of 0+ blocks of the form `)x)`, followed by `0)`, finally followed by arbitrary junk. 96 | // For each `)x)` in the sequence, expands to `m(r,d,x)`, then modifies `d` to contain `s(r,d,x)`, where `r` is the next available loop nesting level. 97 | #define IMPL_SEQFOR_FOR_GUIDExx_A(m, s, f, d, e) IMPL_SEQFOR_CAT(IMPL_SEQFOR_FOR_GUIDExx_A_, e)(m, s, f, d) 98 | #define IMPL_SEQFOR_FOR_GUIDExx_A_(m, s, f, d) IMPL_SEQFOR_FOR_GUIDExx_B(m, s, f, d, 99 | #define IMPL_SEQFOR_FOR_GUIDExx_A_0(m, s, f, d) f(yy, d) IMPL_SEQFOR_NULL( 100 | #define IMPL_SEQFOR_FOR_GUIDExx_B(m, s, f, d, ...) m(yy, d, __VA_ARGS__) IMPL_SEQFOR_FOR_GUIDExx_B_0(m, s, f, s(yy, d, __VA_ARGS__)) 101 | #define IMPL_SEQFOR_FOR_GUIDExx_B_0(...) IMPL_SEQFOR_FOR_GUIDExx_B_1(__VA_ARGS__) 102 | #define IMPL_SEQFOR_FOR_GUIDExx_B_1(m, s, f, d, ...) __VA_ARGS__ IMPL_SEQFOR_FOR_GUIDExx_A(m, s, f, d, 103 | 104 | 105 | // Generated boilerplate for nested loops: 106 | #endif 107 | -------------------------------------------------------------------------------- /tests.cpp: -------------------------------------------------------------------------------- 1 | // Compile-time tests. 2 | // If this file compiles (no linking), the tests passed. 3 | 4 | #ifndef MACRO_SEQUENCE_FOR_H_ 5 | #include "macro_sequence_for.h" 6 | #endif 7 | 8 | #define STR(...) STR_(__VA_ARGS__) 9 | #define STR_(...) #__VA_ARGS__ 10 | 11 | // Compares two strings for equality, ignoring whitespace. 12 | constexpr bool same(const char *a, const char *b) 13 | { 14 | while (true) 15 | { 16 | while (*a == ' ' || *a == '\n') a++; 17 | while (*b == ' ' || *b == '\n') b++; 18 | if (*a != *b) 19 | return false; 20 | if (!*a) 21 | return true; 22 | a++; 23 | b++; 24 | } 25 | } 26 | 27 | #define BODY(n_, d_, ...) (body:n=n_;x=__VA_ARGS__;d=d_) 28 | #define STEP(n_, d_, ...) (step:n=n_;x=__VA_ARGS__;d=d_) 29 | #define FINAL(n_, d_) (final:n=n_;d=d_) 30 | 31 | // Number of iterations: 32 | // 0 33 | static_assert(same(STR(SF_FOR_EACH(BODY, STEP, FINAL, s0,)), "(final:n=0;d=s0)"), "Test: 0 iterations."); 34 | // 1 35 | static_assert(same(STR(SF_FOR_EACH(BODY, STEP, FINAL, s0, (1))), "(body:n=0;x=1;d=s0) (final:n=0;d=(step:n=0;x=1;d=s0))"), "Test: 1 iteration."); 36 | // 3 37 | static_assert(same(STR(SF_FOR_EACH(BODY, STEP, FINAL, s0, (1)(2)(3))), R"( 38 | (body:n=0;x=1;d=s0) 39 | (body:n=0;x=2;d=(step:n=0;x=1;d=s0)) 40 | (body:n=0;x=3;d=(step:n=0;x=2;d=(step:n=0;x=1;d=s0))) 41 | (final:n=0; d=(step:n=0;x=3;d=(step:n=0;x=2;d=(step:n=0;x=1;d=s0)))) 42 | )"), "Test: 3 iteration."); 43 | 44 | // Unusual elements: empty and with commas. 45 | static_assert(same(STR(SF_FOR_EACH(BODY, STEP, FINAL, s0, ()(0)(1,2))), R"( 46 | (body:n=0;x= ;d=s0) 47 | (body:n=0;x=0 ;d=(step:n=0;x= ;d=s0)) 48 | (body:n=0;x=1,2;d=(step:n=0;x=0 ;d=(step:n=0;x= ;d=s0))) 49 | (final:n=0; d=(step:n=0;x=1,2;d=(step:n=0;x=0;d=(step:n=0;x= ;d=s0)))) 50 | )"), "Test: 3 iteration."); 51 | 52 | // Operating with a non-zero nesting level: 53 | static_assert(same(STR(SF_FOR_EACH2(BODY, STEP, FINAL, s0, (1)(2)(3))), R"( 54 | (body:n=3;x=1;d=s0) 55 | (body:n=3;x=2;d=(step:n=3;x=1;d=s0)) 56 | (body:n=3;x=3;d=(step:n=3;x=2;d=(step:n=3;x=1;d=s0))) 57 | (final:n=3; d=(step:n=3;x=3;d=(step:n=3;x=2;d=(step:n=3;x=1;d=s0)))) 58 | )"), "Test: Non-zero nesting level argument."); 59 | 60 | // True nested loops. 61 | #define NESTED_BODY(n_, d_, x_, ...) (n=n_,d=d_,x=x_,SF_CAT(SF_FOR_EACH,n_)(SF_CAT(NESTED_BODY,n_), SF_NULL, SF_NULL, d_, __VA_ARGS__)) 62 | #define NESTED_BODY0(n_, d_, x_, ...) (n=n_,d=d_,x=x_,SF_CAT(SF_FOR_EACH,n_)(SF_CAT(NESTED_BODY,n_), SF_NULL, SF_NULL, d_, __VA_ARGS__)) 63 | #define NESTED_BODY1(n_, d_, x_, ...) (n=n_,d=d_,x=x_,SF_CAT(SF_FOR_EACH,n_)(SF_CAT(NESTED_BODY,n_), SF_NULL, SF_NULL, d_, __VA_ARGS__)) 64 | 65 | static_assert(same(STR(SF_FOR_EACH(NESTED_BODY, SF_NULL, SF_NULL,, (0,)(1,(10,))(2,(20,)(21,(210,)(211,))))), R"( 66 | (n=0,d=,x=0,) 67 | (n=0,d=,x=1, 68 | (n=1,d=,x=10,) 69 | ) 70 | (n=0,d=,x=2, 71 | (n=1,d=,x=20,) 72 | (n=1,d=,x=21, 73 | (n=2,d=,x=210,) 74 | (n=2,d=,x=211,) 75 | ) 76 | ) 77 | )"), "Test: True nested loops."); 78 | 79 | // Predefined macro arguments: 80 | // SF_STATE 81 | static_assert(same(STR(SF_FOR_EACH(BODY, SF_STATE, SF_NULL, s0, (1)(2)(3))), "(body:n=0;x=1;d=s0) (body:n=0;x=2;d=s0) (body:n=0;x=3;d=s0)"), "Test: SF_STATE as the step function."); 82 | // SF_STATE 83 | static_assert(same(STR(SF_FOR_EACH(SF_NULL, SF_STATE, SF_STATE, s0, (1)(2)(3))), "s0"), "Test: SF_STATE as the final function."); 84 | 85 | 86 | 87 | // Emitting text directly from step: 88 | #define BODY2(n_, d_, ...) (body:n=n_;x=__VA_ARGS__;d=d_),b,b 89 | #define STEP2(n_, d_, ...) (step:n=n_;x=__VA_ARGS__;d=d_),s,s 90 | #define FINAL2(n_, d_) (final:n=n_;d=d_),f,f 91 | static_assert(same(STR(SF_FOR_EACH(BODY2, STEP2, FINAL2, s0, (1)(2)(3))), R"( 92 | (body:n=0;x=1;d=s0),b,b s,s 93 | (body:n=0;x=2;d=(step:n=0;x=1;d=s0)),b,b s,s 94 | (body:n=0;x=3;d=(step:n=0;x=2;d=(step:n=0;x=1;d=s0))),b,b s,s 95 | (final:n=0;d=(step:n=0;x=3;d=(step:n=0;x=2;d=(step:n=0;x=1;d=s0)))),f,f 96 | )"), "Test: 0 iterations."); 97 | 98 | 99 | 100 | // Example: generating flags. 101 | #define MAKE_FLAGS(name, seq) enum name {SF_FOR_EACH(FLAGS_BODY, FLAGS_STEP, FLAGS_FINAL, 0, seq)}; 102 | #define FLAGS_BODY(n, d, x) x = 1 << (d), 103 | #define FLAGS_STEP(n, d, x) d+1 104 | #define FLAGS_FINAL(n, d) _mask = (1 << (d)) - 1 105 | 106 | MAKE_FLAGS(E, (a)(b)(c)) 107 | static_assert(E::a == 1, "Test: Generating enum flags."); 108 | static_assert(E::b == 2, "Test: Generating enum flags."); 109 | static_assert(E::c == 4, "Test: Generating enum flags."); 110 | static_assert(E::_mask == 7, "Test: Generating enum flags."); 111 | 112 | // Example: sum. 113 | #define SUM(seq) SF_FOR_EACH(SF_NULL, SUM_STEP, SF_STATE, 0, seq) 114 | #define SUM_STEP(n, d, x) d+x 115 | 116 | static_assert(SUM((1)(2)(3)) == 6, "Test: Integer sum."); 117 | 118 | 119 | // Example: sum. 120 | #define DECL(value, seq) SF_FOR_EACH(DECL_BODY, SF_STATE, SF_NULL, value, seq) 121 | #define DECL_BODY(n, d, x) const int x = d; 122 | DECL(42, (x)(y)(z)) 123 | static_assert(x == 42 && y == 42 && z == 42, "Test: Declaring variables."); 124 | 125 | // Example: nested loop. 126 | #define ARRAYS(seq) SF_FOR_EACH(ARRAYS_BODY, SF_NULL, SF_NULL,, seq) 127 | #define ARRAYS_BODY(n, d, name, values) int name[] = {SF_FOR_EACH2(ARRAYS_BODY2, SF_NULL, SF_NULL,, values)}; 128 | #define ARRAYS_BODY2(n, d, x) x, 129 | 130 | ARRAYS((f,(1)(2)(3))(g,(4)(5))) // int f[] = {1,2,3,}; int g[] = {4,5,}; 131 | --------------------------------------------------------------------------------