Why does gcc break into recursively expanding macros

Good day,

I ran into a strange problem while compiling a very simple C ++ program that uses recursive macro expansion:

#define FINAL(a1, a2, a3) const char *p = "final values are: " #a1 " " #a2 " " #a3;

#define SPLIT(a1, a2) a1, a2
#define BRACES(a1, a2) ( a1, a2 )
#define START(macro, a1, a2) macro BRACES(a1, SPLIT a2)

START(FINAL, 1, (2, 3))

int main(int argc, char* argv[])
{
    std::cout << p << std::endl;
    return 0;
}

      

The program is waiting for "final values: 1 2 3" to be printed. And this is done in Visual Studio 2008.

But I see a problem when trying to compile it with mingw32 gcc-6.3 on Windows 7 and with gcc-5.4 on Linux Ubuntu-16:

$ g++ -I /e/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60 test.cpp
test.cpp:7:24: error: expected constructor, destructor, or type conversion before '(' token
 #define BRACES(a1, a2) ( a1, a2 )
                        ^
test.cpp:8:36: note: in expansion of macro 'BRACES'
 #define START(macro, a1, a2) macro BRACES(a1, SPLIT a2)
                                    ^~~~~~
test.cpp:12:1: note: in expansion of macro 'START'
 START(FINAL, 1, (2, 3))
 ^~~~~

      

It looks like it doesn't depend on the C ++ standard, I've tried -std = C ++ 11 and -std = C ++ 03 with gcc. I've re-read part 16.3 Macro Replacement in the C ++ 11 standard several times, but I think I missed something important there.

What could be wrong with the code here?

One more important thing: BOOST_PP_SEQ_FOR_EACH_I_R from the boost preprocessor library also cannot be compiled, which is more than strange:

#include <iostream>
#include <boost/preprocessor/seq/for_each_i.hpp>

#define FINAL2(r, data, id, value) const char *p ## id = #value;
BOOST_PP_SEQ_FOR_EACH_I_R(_, FINAL2, _, (a)(b)(c))

int main()
{
    std::cout << p1 << std::endl;
    return 0;
}

      

Error output:

$ g++ -I /e/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60 -std=c++03 test2.cpp
In file included from test2.cpp:2:0:
E:/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60/boost/preprocessor/seq/for_each_i.hpp:96:96: error: expected constructor, destructor, or type conversion before '(' token
 #    define BOOST_PP_SEQ_FOR_EACH_I_R_DETAIL_CHECK_EXEC(r, macro, data, seq) BOOST_PP_FOR_ ## r((macro, data, seq, 0, BOOST_PP_SEQ_SIZE(seq)), BOOST_PP_SEQ_FOR_EACH_I_P, BOOST_PP_SEQ_FOR_EACH_I_O, BOOST_PP_SEQ_FOR_EACH_I_M)
                                                                                                ^
E:/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60/boost/preprocessor/control/iif.hpp:32:31: note: in expansion of macro 'BOOST_PP_SEQ_FOR_EACH_I_R_DETAIL_CHECK_EXEC'
 # define BOOST_PP_IIF_1(t, f) t
                               ^
E:/dev-libs/boost/boost_1_60_0-mingw32/include/boost-1_60/boost/preprocessor/control/iif.hpp:25:39: note: in expansion of macro 'BOOST_PP_IIF_1'
 #    define BOOST_PP_IIF_I(bit, t, f) BOOST_PP_IIF_ ## bit(t, f)
                                       ^~~~~~~~~~~~~

      

+3


source to share


3 answers


The problem with the first example seems pretty simple. After the first preprocessing, the interrupt will stop doing what you want:

START(FINAL, 1, (2, 3))
// becomes
FINAL BRACES(1, SPLIT (2, 3))

      



There are no arguments for FINAL

, so the generated code will be broken.

+1


source


As @VTT says, replacement START(FINAL, 1, (2, 3))

is FINAL BRACES(1, SPLIT(2, 3))

; this text is then re-scanned, but it does not have a macro expansion form FINAL

.

You can achieve the desired effect with a different level of indirection (or more accurately, increase the macro):



#define SPLIT(a1, a2) a1, a2
#define BRACES(a1, a2) ( a1, a2 )
#define APPLY(a1, a2) a1 a2
#define START(macro, a1, a2) APPLY(macro, BRACES(a1, SPLIT a2))

      

+1


source


Using the rules specified by the standard, your macro:

START(FINAL, 1, (2, 3))

      

... expands when the argument is replaced with:

FINAL BRACES(1, SPLIT (2, 3))

      

Then rescanning is performed during rescanning and subsequent replacement; at this point it is displayed FINAL

, but there are no arguments after it. Since you only have a functional macro, nothing happens. Thus, CPP is moving forward. BRACES

has two arguments and is a function macro with two arguments, so it expands (the extension SPLIT

is part of that extension), leaving you with the following:

FINAL (1, 2, 3)

      

... and now the CPP is done with BRACES

, so it moves on. There are no steps for backup and evaluation / expansion FINAL

. If you want this to happen, you need an indirect macro (like the one @rici showed you).

That Microsoft CPP is expanding is not surprising; MS CPP is non-standard.

As for the second question (there really should be a separate question):

BOOST_PP_SEQ_FOR_EACH_I_R(_, FINAL2, _, (a)(b)(c))

This call is wrong. Variant macros _R

expect an argument r

that matches the macro sets (this is the first argument). These are numbers; they cannot only be _

. 1

will work; however you don't really need to call the version _R

unless you are doing something recursive / tricky.

0


source







All Articles