5

Is it legal to write recursive macros with __VA_OPT__?

GCC and Clang appear to not replace recursively, but I'm not sure if it's intentional (as __VA_OPT__ support is very recent).

C++ spec (§19.3.1/3: __VA_OPT__):

Otherwise, the replacement consists of the results of the expansion of content as the replacement list of the current function-like macro before rescanning and further replacement

Does the highlighted section above mean recursion isn't possible?


For example, to add a list of variadic macro parameters:

#define RECURSE(mFIRST, ...) + mFIRST __VA_OPT__(RECURSE(__VA_ARGS__))

int main(int argc, char const* const argv[])
    {
        return 1 RECURSE(2, 3, 4);
        // Expected result: "return 1 + 2 + 3 + 4;"
    }

Both GCC and Clang generate RECURSE in their post-preprocessing.

// NOTE: Below is the output from g++ -E
int main(int argc, char const* const argv[])
 {
  return 1 + 2 RECURSE(3, 4);
 }

NOTE: If this is possible, more complex variadic macros could be written fairly easy, such as concatenate, as you can create a custom __VA_NO_OPT__ from __VA_OPT__, which lets you provide completely separate code for 1 and 2+ parameters.

8
  • 4
    Recursive macros don't work in general. Commented Jul 4, 2018 at 5:18
  • 1
    Is that only because they would always recurse infinitely? Because with __VA_OPT__ that's no longer true. Commented Jul 4, 2018 at 5:33
  • 1
    I guess it's due to this: 19.3.4/2: Rescanning and further replacement. "If the name of the macro being replaced is found during this scan of the replacement list (not including the rest of the source file’s preprocessing tokens), it is not replaced". Commented Jul 4, 2018 at 5:40
  • 1
    It's unlikely to get amended. C++ tries very hard to get rid of the needs to use macros each revision. It can't, for various reasons, but recursion is unlikely to be added. __VA_OPT__ is a tiny convenience feature. Recursive macros are one mighty change. Commented Jul 4, 2018 at 6:08
  • 1
    @NicolBolas: AFAICT __VA_OPT__ is a c++2a feature, not a c++17 feature, hence the tag rollback. Apologies if I'm somehow misinformed. Commented Jul 4, 2018 at 17:24

1 Answer 1

5

The answer is yes! To the extent that you could already have recursive macros in C, you can do it in C++20 and __VA_OPT__ makes some things way, way nicer.

Here's an example (with explanation) of what you can do: define a FOR_EACH macro that applies a macro to a bunch of arguments and is way nicer than the horrible things you had to do prior to __VA_OPT__:

#define PARENS () // Note space before (), so object-like macro

#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
#define EXPAND4(arg) arg

#define FOR_EACH(macro, ...)                                    \
  __VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, a1, ...)                         \
  macro(a1)                                                     \
  __VA_OPT__(FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER

FOR_EACH(F, a, b, c, 1, 2, 3)   // => F(a) F(b) F(c) F(1) F(2) F(3)
Sign up to request clarification or add additional context in comments.

6 Comments

I actually use something like this, with F often being a user-defined string literal, so MACRO("dir1", "dir2", "file") becomes "dir1"_X, "dir2"_X, "file"_X. Useful (clearer) when creating constexpr arrays holding each directory name in a path, especially when the UDL name is actually longer than _X. Max 64 items though currently, so still feels a bit hacky.
@xaxazak The code above accepts up to 342 arguments, which is better than 64, and probably enough for anything reasonable. If not, one more line of code for EXPAND5 and you would surely have enough.
Your exponential technique's better than mine (I will adapt it, thanks), but unfortunately, any arbitrary maximum will still feel hacky. There's also a tradeoff with compilation time. If you're specifying 100s of paths each requiring 1000s of macro replacements, and the compiler is tracking these for debugging, it might become noticeable.
Also, this makes a good case for something like __VA_SEP__(<separator>) which would act like __VA_ARGS__ but use <separator> instead of a comma. This might eliminate much of the need (want) for recursion.
@xaxazak __VA_SEP__ is less general than FOR_EACH. If they were going to facilitate this kind of macro, the best thing would be just to build FOR_EACH (or __FOR_EACH__ or something) straight into the preprocessor.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.