I want to force the preprocessor to do some automatic code generation for me. I don\'t need much: just a simple for-loop that contains another for-loop.[1]
I\'ve
With help from the answers here (and studying P99, Chaos, Order, and Cloak) I think I have a reasonably simple and compact proof-of-concept(1). Since I wanted only "repeat" functionality rather than a full blown interpreter, I went with a somewhat different approach than those other solutions. Instead of creating generic "if", "while", or "when" macros, I instead directly used a series of "decrement" macros that expand to the desired macro plus a call to the macro for n-1.
#ifndef _REPEAT_H
#define _REPEAT_H
// Usage: REPEAT_ADD_ONE(macro, times, start_n, macro_args... )
// Recursion allowed if inner macros use REPEAT_ADD_ONE_INNER().
// This demo header only allows 3 layers of recursion and max n=10.
// Sample code at bottom.
#define REPEAT_ADD_ONE(macro, times, start_n, macro_args... ) \
_REPEAT_EXPAND_3(REPEAT_ADD_ONE_INNER(macro, times, start_n, ## macro_args))
#define REPEAT_ADD_ONE_INNER(macro, times, start_n, macro_args... ) \
_REPEAT_ ## times(macro, start_n, _REPEAT_ADD_ONE, ## macro_args)
#define _REPEAT_0(args...) /* empty */
#define _REPEAT_1(macro, n, func, args...) _REPEAT_DEFER(macro)(n, ## args)
#define _REPEAT_2(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_1(m, f(n), f, ## a)
#define _REPEAT_3(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_2(m, f(n), f, ## a)
#define _REPEAT_4(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_3(m, f(n), f, ## a)
#define _REPEAT_5(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_4(m, f(n), f, ## a)
#define _REPEAT_6(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_5(m, f(n), f, ## a)
#define _REPEAT_7(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_6(m, f(n), f, ## a)
#define _REPEAT_8(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_7(m, f(n), f, ## a)
#define _REPEAT_9(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_8(m, f(n), f, ## a)
#define _REPEAT_10(m, n, f, a...) _REPEAT_DEFER(m)(n, ## a); _REPEAT_9(m, f(n), f, ## a)
// ...
#define _REPEAT_ADD_ONE(n, ignore...) _REPEAT_ADD_ONE_ ## n
#define _REPEAT_ADD_ONE_0 1
#define _REPEAT_ADD_ONE_1 2
#define _REPEAT_ADD_ONE_2 3
#define _REPEAT_ADD_ONE_3 4
#define _REPEAT_ADD_ONE_4 5
#define _REPEAT_ADD_ONE_5 6
#define _REPEAT_ADD_ONE_6 7
#define _REPEAT_ADD_ONE_7 8
#define _REPEAT_ADD_ONE_8 9
#define _REPEAT_ADD_ONE_9 10
#define _REPEAT_ADD_ONE_10 11
// ...
#define _REPEAT_EMPTY()
#define _REPEAT_DEFER(token) token _REPEAT_EMPTY()
#define _REPEAT_EXPAND_3(args...) _REPEAT_EXPAND(_REPEAT_EXPAND(_REPEAT_EXPAND(args)))
#define _REPEAT_EXPAND(args...) args
// ...
#endif // _REPEAT_H
#ifdef SAMPLE_CODE
// to generate code: cpp -DSAMPLE_CODE sample.c
// or easier to read: cpp -DSAMPLE_CODE sample.c > out.c; astyle out.c; less out.c
// to compile and run: gcc -Wall -O3 -DSAMPLE_CODE sample.c -o sample
int printf(const char *format, ...);
#define BODY(i) printf("%d\n", i);
void simple(void) {
REPEAT_ADD_ONE(BODY, 5, 1);
}
#define INNER(k, j, i) \
printf("(%d, %d, %d)\n", i, j, k); \
if (i == j && j == k) printf("Match!\n")
#define MIDDLE(j, i) REPEAT_ADD_ONE_INNER(INNER, 2, 2, j, i)
#define OUTER(i) REPEAT_ADD_ONE_INNER(MIDDLE, 3, 0, i)
void recursive(void) {
REPEAT_ADD_ONE(OUTER, 2, 1);
}
int main() {
simple();
recursive();
return 0;
}
#endif // SAMPLE_CODE
I still struggle to understand a lot of the subtleties, but as others pointed out the general rule is that no macro can expand itself. The way around this is to create a macro that expands just to the point where it would call itself, and then put a wrapper around this result to complete the expansion.
The (common) trick that I ended up using is to take advantage of the fact that a function-type macro only expands if it is immediately followed by parentheses. One can use a "defer" macro that puts an "empty" token between the called macro name and its parentheses, and then "expand" this as the argument to another macro.
Since expansion of the arguments takes place in a different context than the initial expansion, the initial macro will expand again. In my solution, one level of expansion is necessary for each level of potential recursion. If playing with the code to understand it, it can be useful to decrease the number of expansions to check the intermediate results.
Thanks for all the help!
(1) True, the standard for "reasonably simple" is quite loose when applied to recursive preprocessor macros. It is quite compact, though.
P99 might provide you with what you are looking for. It has several types of macro iterators, simple ones like P99_UNROLL
, P99_SER
etc and a generic one P99_FOR
.
Im not sure I follow all of your macros there. This answer here(also here now as well) explains how to create a general purpose REPEAT
macro, like this:
#define REPEAT(count, macro, ...) \
WHEN(count) \
( \
OBSTRUCT(REPEAT_INDIRECT) () \
( \
DEC(count), macro, __VA_ARGS__ \
) \
OBSTRUCT(macro) \
( \
DEC(count), __VA_ARGS__ \
) \
)
#define REPEAT_INDIRECT() REPEAT
This takes a count, a macro, and user data. Since the macro passed in is deferred, the REPEAT
macro can be called again directly and recursively. So here is your OUTER
and INNER
repeat macros:
#define OUTER(i, j) { REPEAT(j, INNER, i) }
#define INNER(j, i) if (j == INC(i)) printf("Match\n");
EVAL(REPEAT(2, OUTER, 3))
This will output this:
{
if (0 == 1) printf("Match\n");
if (1 == 1) printf("Match\n");
if (2 == 1) printf("Match\n");
}
{
if (0 == 2) printf("Match\n");
if (1 == 2) printf("Match\n");
if (2 == 2) printf("Match\n");
}
Hopefully, this makes sense.
Vesa Karvonen's "Order" library/language can definitely do this for you. It implements unrestricted recursion and looping in the C preprocessor, and as a really cool bonus dresses it up with the nice concise syntax of a "proper" programming language (to clarify: this is not an alternative preprocessor, it just does a lot of token-pasting to keep its keywords short. It is still pure CPP).
It uses a rather different technique, converting your metaprograms to CPS and then passing them to a single loop construct that has potentially trillions of steps, and executes the metaprogram in a strictly linear fashion. Loops and recursive functions can therefore be nested as deeply as you like because they don't have separate drivers that need to interact and paint each other blue.
Yes really, someone implemented a full virtual machine and interpreter using CPP macros. It's intimidating.
(EDIT: try the archived version if Rosetta Code has stopped working for you too.)