3

Good day,

I've faced with a strange problem while compiling very simple C++ program which leverages recursive macros 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 expecting to print "final values are: 1 2 3" text. And it does on a Visual Studio 2008.

But I see an issue 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 a C++ standard, I've tried -std=c++11 and -std=c++03 with gcc. I've reread part 16.3 "Macro replacement" of C++ 11 standard a few times, but I guess I've missed something important there.

What can be wrong here with the code?

One more important thing: BOOST_PP_SEQ_FOR_EACH_I_R from boost preprocessor library can't be compiled too, 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)
                                       ^~~~~~~~~~~~~
  • Hmm. g++ 5.4.0 expands the line to `FINAL ( 1, 2, 3 )` - note the space after `FINAL`... – aschepler Jul 01 '17 at 20:15
  • 1
    I don't think you're using `BOOST_PP_SEQ_FOR_EACH_I_R` correctly. The `BOOST_PP_*_R` macros seem to be for when you need a loop within a loop, and it looks like the first argument is supposed to be another loop macro. – aschepler Jul 01 '17 at 20:59
  • Got your boost program working: http://coliru.stacked-crooked.com/a/e946e7dc06ad8e30 You must not use the `_R` macro, and you need `BOOST_PP_CAT` and `BOOST_PP_STRINGIZE` to prevent the `##` and `#` operators from acting too soon. – aschepler Jul 01 '17 at 21:09
  • First argument is a number. This really should be two questions though (after all, it is two questions). – H Walters Jul 01 '17 at 21:11

3 Answers3

1

The problem with first example seems to be rather straightforward. After first expansion preprocessing will stop doing what you want it to do:

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

There are no arguments for FINAL so generated code will be messed up.

user7860670
  • 32,142
  • 4
  • 44
  • 75
1

As @VTT says, the replacement for START(FINAL, 1, (2, 3)) is FINAL BRACES(1, SPLIT(2, 3)); that text is then rescanned, but it does not have the form of a macro expansion of FINAL.

You can achieve the effect you want with another level of indirection (or macro expansion, to be more precise):

#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))
rici
  • 201,785
  • 23
  • 193
  • 283
  • When a compiler replaces BRACES() macro, it gets correct calling for the last macro: `FINAL (1, 2, 3)` which can be observed using '-E' command-line option to get a preprocessor output. But the question here is why the compiler doesn't make the last step with replacing FINAL macro? And why Microsoft compiler does it? – Vyacheslav Grigoryev Jul 01 '17 at 23:44
  • @vyacheslav: when the compiler substitutes the `BRACES` macro, it has already passed over `FINAL`. It won't pass over it again; for the macro to be substituted, it needs to be visibly called in the macro replacement after the parameters are substituted with the fully expanded arguments. The MS compiler's preprocessing fails to comply with many aspects of the standard; I don't have a good answer as to why that is the case. – rici Jul 02 '17 at 03:25
0

Using the rules specified by the standard, your macro:

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

...expands during argument substitution to:

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

This is then rescanned during rescan and further replacement; during this step, FINAL is seen, but it doesn't have arguments after it. Since you only have a function-like macro, nothing happens. So the CPP moves along. BRACES however has two arguments, and is a two argument function-like macro, so it expands (SPLIT's expansion is part of this expansion), leaving you with this:

FINAL (1, 2, 3)

...and now the CPP is done with BRACES, so it moves along further. There's no step to back up and evaluate/expand FINAL. If you want that to happen, you need an indirect macro (such as the one @rici showed you).

That Microsoft's CPP expands this isn't surprising; MS's CPP is non-standard.

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

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

That call is wrong. The _R variant macros expect an r argument that corresponds to macro sets (this is the first argument). These are numbers; they can't just be _. 1 would work; however, you don't actually need to call the _R version unless you're doing something recursive/complex.

H Walters
  • 2,346
  • 1
  • 8
  • 12