How to Have Recursive Macros

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:


    1. The arguments are fully evaluated:
    • FOO() -> 1
    • BAR() -> 2


    1. The evaluated values are substituted into the macro body:
    • BAZ(1, 2) -> MIAU(1, 2)


    1. 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:


    1. 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


    1. Substitution into the macro body:

      FOO(BAR) -> BAR()


    1. Evaluation of the body:

      BAR() -> 12

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 in 1 + FOO
  • BAR would result in 1 + 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)

  1. 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) becomes FOR_EACH_AGAIN_R () (MY_FUNC, B, C) (not evaluated further due to FOR_EACH_AGAIN_R not being a direct call)
      • A -> A
  • 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 in MY_FUNC which was called by FOR_EACH_HELPER_R and we are trying to call FOR_EACH_HELPER_R again here)

    so the FOR_EACH_HELPER_R will be marked as don't try to evaluate this

    additionally the MY_FUNC parameter will also be marked (since we're in MY_FUNC)

  1. Every EXPAND round after that
  • preprocessor tries to evaluate the current expression:

    (FOR_EACH_HELPER_R (MY_FUNC, B, C) | A)
    but FOR_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



Leave a reply



Submit