Can we have recursive macros?
Your compiler probably provides an option to only pre-process, not actually compile. This is useful if you are trying to find a problem in a macro. For example using g++ -E
:
> g++ -E recursiveMacro.c
# 1 "recursiveMacro.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "recursiveMacro.c"
void main ()
{
int a=5;
cout<<"result: "<< ((5==1)? 1 : pr(5 -1)) <<endl;
getch();
}
As you can see, it is not recursive. pr(x)
is only replaced once during pre-processing. The important thing to remember is that all the pre-processor does is blindly replace one text string with another, it doesn't actually evaluate expressions like (x == 1)
.
The reason your code will not compile is that pr(5 -1)
was not replaced by the pre-processor, so it ends up in the source as a call to an undefined function.
C-preprocessor recursive macro
Here's the answer in case somebody wants to do the same.
#define _PP_0(_1, ...) _1 // (a,b,c,d) => a
#define _PP_X(_1, ...) (__VA_ARGS__) // (a,b,c,d) => (b,c,d)
//for each a in __VA_ARGS__ do f(a,x)
//where x is some parameter passed to PP_TRANSFORM
#define PP_TRANSFORM(f,x,...) \
PP_JOIN(PP_TRANSFORM_,PP_NARG(__VA_ARGS__))(f,x,(__VA_ARGS__))
#define PP_TRANSFORM_0(...)
#define PP_TRANSFORM_1( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_0( f,x,_PP_X a)
#define PP_TRANSFORM_2( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_1( f,x,_PP_X a)
...
#define PP_TRANSFORM_51(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_50( f,x,_PP_X a)
...
#define PP_TRANSFORM_99(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_98(f,x,_PP_X a)
#define PP_TRANSFORM_100(f,x,a)f(_PP_0 a,x) PP_TRANSFORM_99(f,x,_PP_X a)
where PP_NARG
is the macro that counts number of arguments and PP_JOIN
is the macro that joins tokens (that is PP_JOIN(a,b) => ab
). You'll also need to patch that PP_NARG
if you want to be able to process more than 64 arguments.
Now, back to the original question. Solution using the PP_TRANSFORM
is:
#define FUNCTION(name, dummy) void name();
#define FUNCTION_TABLE(...) PP_TRANSFORM(FUNCTION,dummy,__VA_ARGS__)
if you want to generate c++ implementation functions then you can use that opaque x parameter of PP_TRANSFORM
:
#define FUNCTION_CPP(name, class) void class::name(){}
#define FUNCTION_TABLE_CPP(...) PP_TRANSFORM(FUNCTION_CPP,MyClass,__VA_ARGS__)
All this works equally well with GCC and MSVC preprocessors; PP_TRANSFORM_NN doesn't use __VA_ARGS__
to avoid separate implementations of 100 defines for GCC and MSVC
Variadic recursive preprocessor macros - is it possible?
No, because the preprocessor only takes one "swipe" at the file. There's no way to get it to recursively define macros.
The only code that I've seen do something like this was not variadic, but used default values the user had to pass:
x = MAX_OF_8 (a, b, -1, -1, -1, -1, -1, -1)
assuming all values were non-negative.
Inline functions should give you the same for C++ at least. As you state, it's probably better left to a function with variable arguments similar to printf()
.
How do recursive macro definitions get evaluated
Your example does not work.
It may work in an interpreter. But with a compiler you'll see an endless loop during compilation.
CL-USER 23 > (defun test (foo)
(sum-int-seq 5))
TEST
Let's use the LispWorks interpreter:
CL-USER 24 > (test :foo)
15
Let's try to compile the function:
CL-USER 25 > (compile 'test)
Stack overflow (stack size 15997).
1 (continue) Extend stack by 50%.
2 Extend stack by 300%.
3 (abort) Return to level 0.
4 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
So, now the next question: why does it work in the interpreter, but the compiler can't compile it?
Okay, I'll explain it.
Let's look at the interpreter first.
- it sees
(sum-int-seq 5)
. - it macroexpands it to
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1)))))
. - it then evaluates above form. It determines that it needs to compute
(+ 5 (SUM-INT-SEQ (- 5 1)))
. For that it needs to macroexpand(SUM-INT-SEQ (- 5 1))
. - eventually it will expand into something like
(cond ((EQUAL 0 (- (- (- (- (- 5 1) 1) 1) 1) 1)) 0) ...
. Which then will return 0 and the computation can use this result and add the other terms to it.
The interpreter takes the code, evaluates what it can and macroexpands if necessary. The generated code is then evaluated or macroexpanded. And so on.
Now let's look at the compiler.
- it sees (sum-int-seq 5) and macroexpands it into
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1)))))
. - now the macroexpansion will be done on the subforms, eventually.
- the compiler will macroexpand
(SUM-INT-SEQ (- 5 1))
. note that the code never gets evaluated, only expanded. - the compiler will macroexpand
(SUM-INT-SEQ (- (- 5 1) 1))
and so forth. finally you'll see a stack overflow.
The compiler walks (recursively compiles / expands) the code. It may not execute the code (unless it does optimizations or a macro actually evaluates it explicitly).
For a recursive macro you'll need to actually count down. If you eval inside the macro, then something like (sum-int-seq 5)
can made work. But for (defun foo (n) (sum-int-seq n))
this is hopeless, since the compiler does not know what the value of n is.
Recursive C macro not expanded
You can't get the behaviour you want. The C preprocessor is, by design, not turing complete.
You can use multiple macros to get multiple replacements, but you will not achieve true recursion with an arbitrary number of replacements.
How to expand a recursive macro via __VA_OPT__ in a nested context
Your basic FOR_EACH_R
is correct, what's causing the problem is the call to func
within your FOR_EACH_HELPER_R
macro.
You can verify this by temporarely removing it:
/* ... */
#define FOR_EACH_HELPER_R(func, sub, ...) (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), sub)
/* ... */
NEST_RECURSIVE(A, B, C)
godbolt example
would result in:
(((, C), B), A)
1. How Macro Evaluation works
Macro Evaluation is not very intuitive, so i'll quickly explain the 2 concepts that are important to this answer:
1.1 function-like macro evaluation order
Example:
#define FOO() 1
#define BAR() 2
#define BAZ(A, B) MIAU(A, B)
#define MIAU(A, B) B A
BAZ(FOO(), BAR())
When the preprocessor sees the call to BAZ()
the following things will happen:
- The arguments are fully evaluated:
FOO()
-> 1BAR()
-> 2
- The evaluated values are substituted into the macro body:
BAZ(1, 2)
->MIAU(1, 2)
- The macro body is fully evaluated again
MIAU(1, 2)
->2 1
So arguments can be potentially evaluated twice - once before they are substituted into the macro body and then again when the body is evaluated.
This is also why you can pass macros as arguments to other macros, e.g.:
#define FOO(fn) fn()
#define BAR() 12
FOO(BAR)
Here the following things would happen:
- Preprocessor fully evaluates arguments:
BAR
does name a macro, but it's not a macro call. So the preprocessor will not evaluate it and treats it as text:BAR
- Preprocessor fully evaluates arguments:
- Substitution into the macro body:
FOO(BAR)
->BAR()
- Substitution into the macro body:
- Evaluation of the body:
BAR()
->12
- Evaluation of the body:
Your EXPAND
macro also uses this to repeatedly force evaluation of an expression.
1.2 You can't call a macro from itself
e.g.:
#define FOO 1 + FOO
FOO
#define BAR 1 + BAZ
#define BAZ BAR
BAR
FOO
would result in1 + FOO
BAR
would result in1 + BAR
Essentially while the preprocessor is evaluating a given macro, e.g. BAR
, any occurance of BAR
within it (or in one of the macros it calls) will be marked as don't try to evaluate this - EVER.
So basically once a macro sees it's own name it's game over.
2. Why your example doesn't work
Let's walk through the evaluation of your FOR_EACH_R
macro:
(i'll leave out the EXPAND
macros for simplicity)
- First
EXPAND
round
- first we start with
FOR_EACH_HELPER_R(MY_FUNC, A, B, C)
- substition into body:
MY_FUNC(FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C), A)
- then evaluate the body
- the preprocessor sees the call to
MY_FUNC
, so both arguments will be evaluated:FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C)
becomesFOR_EACH_AGAIN_R () (MY_FUNC, B, C)
(not evaluated further due toFOR_EACH_AGAIN_R
not being a direct call)A
->A
- the preprocessor sees the call to
- substitute into
MY_FUNC
body:MY_FUNC((FOR_EACH_AGAIN_R () (MY_FUNC, B, C)), A)
->(FOR_EACH_AGAIN_R () (MY_FUNC, B, C) | A)
- evaluate body:
(FOR_EACH_AGAIN_R () (MY_FUNC, B, C) | A)
->(FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)
->
preprocessor detects recursion (we are inMY_FUNC
which was called byFOR_EACH_HELPER_R
and we are trying to callFOR_EACH_HELPER_R
again here)
so theFOR_EACH_HELPER_R
will be marked as don't try to evaluate this
additionally theMY_FUNC
parameter will also be marked (since we're inMY_FUNC
)
- Every
EXPAND
round after that
- preprocessor tries to evaluate the current expression:
(FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)
butFOR_EACH_HELPER_R
is marked as don't try to evaluate this, so the call is ignored and nothing gets replaced.
-> You end up with (FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)
as output
3. How to fix it
The big problem is that you pass FOR_EACH_AGAIN_R(...)
as an argument to your func
, which will evaluate that part twice (once as argument, once in the body), so the preprocessor sees the recursive call and stops.
You can partially fix it by delaying the FOR_EACH_AGAIN_R
by another evaluation cycle, e.g.:
/* ... */
#define FOR_EACH_HELPER_R(func, sub, ...) func (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), sub)
#define FOR_EACH_AGAIN_R() FOR_EACH_AGAIN_R_IMPL PARENS
#define FOR_EACH_AGAIN_R_IMPL() FOR_EACH_HELPER_R
/* ... */
godbolt example
This would result in:
(MY_FUNC (MY_FUNC (, C), B) | A)
Now the loop got fully expanded, however there's still a recursion problem with MY_FUNC
.
The underlying issue here is that one of the parameters to MY_FUNC
will contain MY_FUNC
, e.g.:
MY_FUNC((FOR_EACH_AGAIN_R PARENS (MY_FUNC, B, C)), A)
so once the preprocessor substitutes MY_FUNC
into MY_FUNC
that token will be immediatly marked as don't ever try to evaluate that again.
So the MY_FUNC
chain gets stuck after the first call.
It would be a lot easier if you wouldn't need recursive calls, e.g.:
/* ... */
#define FOR_EACH_HELPER_R(func, sub, ...) __VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, __VA_ARGS__)), func(sub)
/* ... */
#define MY_FUNC(var) (var)
/* ... */
godbolt example
would work without problems (result would be , (C), (B), (A)
)
If you absolutely need the recursive calls, then there is only one way:
You need to make sure that MY_FUNC
never gets to see FOR_EACH_HELPER_R
& MY_FUNC
.
But given that each invocation of MY_FUNC
needs the result of the previous invocation, your only option is to build the macro in such a way that all the MY_FUNC
calls are evaluated at once.
e.g. you need to build FOR_EACH_HELPER_R
in such a way that eventually you're left with:
MY_FUNC(MY_FUNC(MY_FUNC(, C), B), A)
so that the recursive calls will be evaluated correctly.
The easiest way to ensure this is to use the same delay trick you use for FOR_EACH_AGAIN_R
, e.g. with a set of macros like this:
#define DELAY6(...) DELAY6_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY5(...) DELAY5_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY4(...) DELAY4_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY3(...) DELAY3_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY2(...) DELAY2_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY1(...) DELAY1_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY0(...) __VA_ARGS__
#define DELAY6_IMPL() DELAY5
#define DELAY5_IMPL() DELAY4
#define DELAY4_IMPL() DELAY3
#define DELAY3_IMPL() DELAY2
#define DELAY2_IMPL() DELAY1
#define DELAY1_IMPL() DELAY0
So DELAY6
will delay it for 6 evaluations, DELAY5
for 5, etc...
Then you can use it to delay the call to MY_FUNC
, e.g.:
#define FOR_EACH_R(func, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER_R(func, DELAY6, __VA_ARGS__)))
#define FOR_EACH_HELPER_R(func, del, sub, ...) del(func) (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, del(), __VA_ARGS__)), sub)
notice that we're passing del()
, not del
to the next iteration of FOR_EACH_HELPER_R
, this will effectively result in the next lower DELAY*
function being passed (so that all the delays resolve in a single evaluation)
With NEST_RECURSIVE(A, B, C, D, E, F, G)
this would evaluate as following:
DELAY5_IMPL () (MY_FUNC) (DELAY5_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY5_IMPL () , C, D, E, F, G), B), A)
->
DELAY4_IMPL () (MY_FUNC) (DELAY4_IMPL () (MY_FUNC) (DELAY4_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY4_IMPL () , D, E, F, G), C), B), A)
->
DELAY3_IMPL () (MY_FUNC) (DELAY3_IMPL () (MY_FUNC) (DELAY3_IMPL () (MY_FUNC) (DELAY3_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY3_IMPL () , E, F, G), D), C), B), A)
->
DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (DELAY2_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY2_IMPL () , F, G), E), D), C), B), A)
->
DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (DELAY1_IMPL () (MY_FUNC) (FOR_EACH_AGAIN_R () (MY_FUNC, DELAY1_IMPL () , G), F), E), D), C), B), A)
->
((((((( | G) | F) | E) | D) | C) | B) | A)
godbolt example
Notice that MY_FUNC
didn't get called until the last evaluation round - this essentially ensures that all MY_FUNC calls are evaluated at once and we don't get any problems with recursive macro calls.
You'll have to define a lot of DELAY_
macros though for this to work (1 more delay macro for each additional parameter to FOR_EACH_R
)
Full code example: godbolt
#define PARENS ()
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__
#define DELAY6(...) DELAY6_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY5(...) DELAY5_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY4(...) DELAY4_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY3(...) DELAY3_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY2(...) DELAY2_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY1(...) DELAY1_IMPL PARENS __VA_OPT__((__VA_ARGS__))
#define DELAY0(...) __VA_ARGS__
#define DELAY6_IMPL() DELAY5
#define DELAY5_IMPL() DELAY4
#define DELAY4_IMPL() DELAY3
#define DELAY3_IMPL() DELAY2
#define DELAY2_IMPL() DELAY1
#define DELAY1_IMPL() DELAY0
#define FOR_EACH_R(func, ...) __VA_OPT__(EXPAND(FOR_EACH_HELPER_R(func, DELAY6, __VA_ARGS__)))
#define FOR_EACH_HELPER_R(func, del, sub, ...) del(func) (__VA_OPT__(FOR_EACH_AGAIN_R PARENS (func, del(), __VA_ARGS__)), sub)
#define FOR_EACH_AGAIN_R() FOR_EACH_HELPER_R
#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)
NEST_RECURSIVE(A, B, C, D, E, F, G)
4. Potentially better approaches
4.1 Simple macro chain
The above solution requires a very good understanding of how macros are evaluated to understand what's happending. You could also choose an easier approach by just defining a bunch of macros, like boost does for example
e.g.:
#define FOR_EACH_ERROR()
#define FOR_EACH_R(fn, ...) __VA_OPT__(FOR_EACH_R_IMPL_0(fn, __VA_ARGS__))
#define FOR_EACH_R_IMPL_0(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_1(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_1(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_2(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_2(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_3(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_3(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_4(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_4(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_5(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_5(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_6(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_6(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_7(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_7(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_8(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_8(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_9(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_9(fn, el, ...) fn(__VA_OPT__(FOR_EACH_R_IMPL_10(fn, __VA_ARGS__)), el)
#define FOR_EACH_R_IMPL_10(...) FOR_EACH_ERROR("Shenanigans!")
#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)
NEST_RECURSIVE(A, B, C, D, E, F, G)
godbolt example
this is easier to understand & also very easy to expand (just add more macros)
4.2 recursive left fold
If you want a recursive version to reduce the number of lines you need to write, you could accomplish that by using a left fold, e.g.:
// Recursive Left Fold
#define FOR_EACH_L(fn, ...) __VA_OPT__(FOR_EACH_APPLY0(FOR_EACH_RESULT, FOR_EACH_L_4(fn,,__VA_ARGS__)))
#define FOR_EACH_L_4(fn, res, ...) FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_3(fn, res, ...) FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_2(fn, res, ...) FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_1(fn, res, ...) FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_0(fn, res, ...) fn, FOR_EACH_FIRST(__VA_OPT__(fn(res, FOR_EACH_FIRST(__VA_ARGS__)), ) res) __VA_OPT__(FOR_EACH_TAIL(__VA_ARGS__))
#define FOR_EACH_APPLY4(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY3(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY2(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY1(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY0(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_FIRST(el, ...) el
#define FOR_EACH_TAIL(el, ...) __VA_OPT__(, __VA_ARGS__)
#define FOR_EACH_RESULT(fn, res, ...) res
godbolt example
Left-folds are easier to implement than right-folds, because getting the first element of __VA_ARGS__
is a lot easier than getting the last one (FOR_EACH_FIRST
in the above example).
The version provided above can handle up to 81 parameters, if you need more just create more versions of the FOR_EACH_L_*
& FOR_EACH_APPLY*
macros. (each additional one of those macros triples the max number of arguments it can handle)
You do need to provide the arguments in reverse order though, since it's a left fold, e.g.:
NEST_RECURSIVE(A, B, C, D, E, F, G)
// would result in ((((((( | A) | B) | C) | D) | E) | F) | G)
If you need a right fold you could implement it by reversing the arguments and then calling the left fold variant we created above.
e.g.:
// Reverse args
#define PARENS ()
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__
#define REVERSE(...) __VA_OPT__(EXPAND(REVERSE_HELPER(__VA_ARGS__)))
#define REVERSE_HELPER(el, ...) __VA_OPT__(REVERSE_AGAIN PARENS (__VA_ARGS__), ) el
#define REVERSE_AGAIN() REVERSE_HELPER
godbolt example
(this also only works for up to 81 arguments, you can add more EXPAND*
macros to increase the number of args it can handle)
example:
REVERSE(A, B, C, D, E, F, G)
// would result in G, F, E, D, C, B, A
and then you could implement the right fold like this:
// Right fold
#define FOR_EACH_R(fn, ...) __VA_OPT__(FOR_EACH_R_APPLY(FOR_EACH_L, fn, REVERSE(__VA_ARGS__)))
#define FOR_EACH_R_APPLY(fn, ...) fn(__VA_ARGS__)
godbolt example
which finally gives you the expected result (for up to 81 arguments), e.g.:
NEST_RECURSIVE(A, B, C, D, E, F, G)
// would result in: ((((((( | G) | F) | E) | D) | C) | B) | A)
Full Code: godbolt
// Reverse args
#define PARENS ()
#define EXPAND(...) EXPAND4(EXPAND4(EXPAND4(EXPAND4(__VA_ARGS__))))
#define EXPAND4(...) EXPAND3(EXPAND3(EXPAND3(EXPAND3(__VA_ARGS__))))
#define EXPAND3(...) EXPAND2(EXPAND2(EXPAND2(EXPAND2(__VA_ARGS__))))
#define EXPAND2(...) EXPAND1(EXPAND1(EXPAND1(EXPAND1(__VA_ARGS__))))
#define EXPAND1(...) __VA_ARGS__
#define REVERSE(...) __VA_OPT__(EXPAND(REVERSE_HELPER(__VA_ARGS__)))
#define REVERSE_HELPER(el, ...) __VA_OPT__(REVERSE_AGAIN PARENS (__VA_ARGS__), ) el
#define REVERSE_AGAIN() REVERSE_HELPER
// Recursive Left Fold
#define FOR_EACH_L(fn, ...) __VA_OPT__(FOR_EACH_APPLY0(FOR_EACH_RESULT, FOR_EACH_L_4(fn,,__VA_ARGS__)))
#define FOR_EACH_L_4(fn, res, ...) FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, FOR_EACH_APPLY4(FOR_EACH_L_3, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_3(fn, res, ...) FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, FOR_EACH_APPLY3(FOR_EACH_L_2, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_2(fn, res, ...) FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, FOR_EACH_APPLY2(FOR_EACH_L_1, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_1(fn, res, ...) FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, FOR_EACH_APPLY1(FOR_EACH_L_0, fn, res __VA_OPT__(, __VA_ARGS__))))
#define FOR_EACH_L_0(fn, res, ...) fn, FOR_EACH_FIRST(__VA_OPT__(fn(res, FOR_EACH_FIRST(__VA_ARGS__)), ) res) __VA_OPT__(FOR_EACH_TAIL(__VA_ARGS__))
#define FOR_EACH_APPLY4(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY3(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY2(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY1(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_APPLY0(fn, ...) fn(__VA_ARGS__)
#define FOR_EACH_FIRST(el, ...) el
#define FOR_EACH_TAIL(el, ...) __VA_OPT__(, __VA_ARGS__)
#define FOR_EACH_RESULT(fn, res, ...) res
// Right fold
#define FOR_EACH_R(fn, ...) __VA_OPT__(FOR_EACH_R_APPLY(FOR_EACH_L, fn, REVERSE(__VA_ARGS__)))
#define FOR_EACH_R_APPLY(fn, ...) fn(__VA_ARGS__)
// For testing
#define MY_FUNC(nested, var) (nested | var)
#define NEST_RECURSIVE(...) FOR_EACH_R(MY_FUNC, __VA_ARGS__)
NEST_RECURSIVE(A, B, C, D, E, F, G)
Recursive macro Elixir
TL;DR: this is impossible.
Macros in elixir are not what you expect them to be. When a compiler sees a macro, it calls it during a compilation time and injects the AST it returned in the place of where it was called. That said, recursive macros would always lead to the infinite loop at the compilation stage.
Put IO.puts("something")
before quote do
instruction and you’ll see it to be printed infinitely, once per subsequent call to expand the macro.
You can use @compile {:inline, test_rec: 1}
to achieve the behaviour you are after. For better understanding macros, you probably should read Macros
section in the Elixir Guide in general and this excerpt in particular:
Macros are harder to write than ordinary Elixir functions and it’s considered to be bad style to use them when they’re not necessary. So write macros responsibly.
How to define recursive variadic macros?
Apparently, MS VC++ behaves differently than expected with the recursive expansion of __VA_ARGS__
that includes multiple comma-separated arguments. When expanded in a nested macro, the the compiler treats the whole arg list as one argument.
Examples can also be seen here and here.
However, @Ise Wisteria suggested the EXPAND(x)
macro as a workaround. Using this tip, I changed my code to:
#define EXPAND( x ) x
#define B_TO_UINT1(b00) (((uint32_t) b00 << 0))
#define B_TO_UINT2(b01, ...) (((uint32_t) b01 << 1) | EXPAND( B_TO_UINT1(__VA_ARGS__)))
#define B_TO_UINT3(b02, ...) (((uint32_t) b02 << 2) | EXPAND( B_TO_UINT2(__VA_ARGS__)))
#define B_TO_UINT4(b03, ...) (((uint32_t) b03 << 3) | EXPAND( B_TO_UINT3(__VA_ARGS__)))
// ...
Now the build errors are eliminated.
Note: I am not explicitly saying "a bug", b/c there may be a place for understanding the standard the MS way, as described here.
Related Topics
Std::Endl Is of Unknown Type When Overloading Operator≪≪
What Is the Partial Ordering Procedure in Template Deduction
Why Aren't Pointers Initialized With Null by Default
_File_, _Line_, and _Function_ Usage in C++
Std::Cin.Getline( ) Vs. Std::Cin
Boost Asio Async_Write: How to Not Interleaving Async_Write Calls
How Is a Variable At the Same Address Producing 2 Different Values
How to Build Qt For Visual Studio 2010
What Requirements Must Std::Map Key Classes Meet to Be Valid Keys
Difference Between Iostream and Iostream.H
When Can Outer Braces Be Omitted in an Initializer List
Why Can't I Compile an Unordered_Map With a Pair as Key
Displaying the #Include Hierarchy For a C++ File in Visual Studio
How to Loop Through a C++ Map of Maps
Double Precision - Decimal Places
Why Are Function Pointers and Data Pointers Incompatible in C/C++
Smart Pointers (Boost) Explained
Why Would the Behavior of Std::Memcpy Be Undefined For Objects That Are Not Triviallycopyable