Why gcc breaks in recursively expanding macros - c++

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)
^~~~~~~~~~~~~

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.

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))

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.

Related

error: macro "assert" passed 2 arguments, but takes just 1

vector<string> foo(vector<string> s) { return s; }
assert(foo(vector<string>{"hello", "world"}) ==
vector<string>{"hello", "world"});
error: macro "assert" passed 2 arguments, but takes just 1
error: ‘assert’ was not declared in this scope
maybe define assert in gcc 11.1.0
# define assert(expr) \
(static_cast <bool> (expr) \
? void (0) \
: __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION))
compiler flag is
-Wall -std=c++20
The preprocessor only has a primitive understanding of C++'s syntax, and in particular it sees any commas not enclosed in parentheses as argument separators. There are two commas in your assert call, and only one is enclosed in parentheses, so the macro thinks it's getting two arguments as follows
foo(vector<string>{"hello", "world"}) == vector<string>{"hello"
"world"});
Wrap the expression in parentheses to prevent this.
// Note: Double parens
assert((foo(vector<string>{"hello", "world"}) ==
vector<string>{"hello", "world"}));

The asterisk is not a character constant?

foo.cpp:
#define ID A
#if ID == A
#warning "hello, world"
#endif
Compilation with g++ -c foo.cpp works fine: (g++ v8.2.0)
foo.cpp:3:2: warning: #warning "hello, world" [-Wcpp]
#warning "hello, world"
^~~~~~~
Now, if I replace #define ID A with #define *, then I get:
foo.cpp:1:12: error: operator '*' has no left operand
#define ID *
^
foo.cpp:2:5: note: in expansion of macro ‘ID’
#if ID == A
^~
What is so special about *? Why does it fail in the #if expression?
There are two things of note in your post. The first, is that it doesn't work as you think. This will produce the warning too
#define ID B
#if ID == A
#warning "hello, world"
#endif
The reason is that in the context of #if the preprocessing tokens ID and A are taken as macros and are expanded. Since A is not defined, it is "expanded" to 0. So is ID via the expansion ID -> B -> 0. So the condition is true here as well.
This also answers why * causes an error. It cannot be expanded further (on account of not being a valid identifier), and therefore you get the comparison * == 0, which is nonsense.
Since your title implies you seek to compare against a character constant, the way to do that would be to define ID to expand into the token sequence of a character constant.
#define ID 'A'
#if ID == 'A'
It should now work as expected. As will #define ID '*'
#if does not what you think it is doing.
In your first example, it tries to evaluate 0 == 0, which is a valid expression with a value of true.
In your second example, it tries to evaluate * == 0, which is not a valid expression.

Why do I get "conflicting declaration" error using two and more typedef-based static asserts with gcc and template function?

I have the following code (no, the template is not instantiated anywhere):
#define cAssert( expr ) typedef char __C_ASSERT__[( expr )?1:-1]
template<int t>
void f()
{
cAssert( t != 0 );
cAssert( t != 2 );
}
which compiles just fine with Visual C++ (when t is neither 0 nor 2 of course). Yet gcc (4.9.0 and earlier - you can test them on http://gcc.godbolt.org/) outputs this:
In function ‘void f()’:
1 : error: conflicting declaration ‘typedef char __C_ASSERT__ [((t != 2) ? 1 : (-1))]’
#define cAssert( expr ) typedef char __C_ASSERT__[( expr )?1:-1]
If I only leave a single cAssert() (any of the two) - it compiles fine. The problem is there only when I have both of them.
Why would gcc reject this code and how can I fix this?
I do not know why this does not work.
One and a half fix:
#define cAssertbraces( expr ) typedef char __C_ASSERT_BRACES__[( expr )?1:-1]
template<class T>
void cAssert(T v1, T v2) { {cAssertbraces( v1 != v2 );} }
template<int t>
void f()
{
{cAssertbraces( t != 0 );}
{cAssertbraces( t != 2 );}
cAssert(t, 0);
cAssert(t, 2);
}
or, use __LINE__ like this:
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(!!(COND))*2-1]
#define COMPILE_TIME_ASSERT3(X,L) STATIC_ASSERT(X,static_assertion_at_line_##L)
#define COMPILE_TIME_ASSERT2(X,L) COMPILE_TIME_ASSERT3(X,L)
#define COMPILE_TIME_ASSERT(X) COMPILE_TIME_ASSERT2(X,__LINE__)
...
COMPILE_TIME_ASSERT( t != 2 );
which I took from this question (nice answers there, should check).
The reason that GCC rejects the code is essentially that, after the macro has been expanded, you have a differing sequence of tokens comprising the declaration of the type. Consider a much easier example:
template <unsigned t>
void f()
{
using type = int[t + 0];
using type = int[t ]; // Adding a +0 here makes GCC compile it fine
}
GCC complains. However, this was covered by core issue 422, where CWG concluded that
We believe that all these cases should be allowed, and that errors should be required only when an instance of the template is generated. The current standard wording does not seem to disallow such cases, so no change is required.
That is, GCC is wrong.

C/C++ prevent macro expansion inside macro

Let's consider:
#define PARENTHESIS1 (
#define PARENTHESIS2 )
#define macro_test_0(arg1, arg2) int arg1 arg2
#define macro_test_1(arg1, arg2) macro_test_0(arg1, arg2)
macro_test_0(PARENTHESIS1, PARENTHESIS2 ;) //->works fine
macro_test_1(PARENTHESIS1, PARENTHESIS2 ;) //doesn't work
For macro_test_1 I have error message: "Macro argument mismatch", "Too few arguments provided to function-like invocation method", "Use of undeclared identifier 'macro_test_0' ".
Thing is, for macro_test_0, example gives:
int ( ) ;
which is ok, but macro_test_1 example gives (if I'm correct):
macro_test_0((,) ;)
which is obviously wrong. I'd like arg1 and arg2 of the macro to prevent expansion, in order to keep:
macro_test0(PARENTHESIS1, PARENTHESIS2 ;)
I guess it's related to macro expansion order, but is there a way or trick to achieve this ? I tried several things such as artificial (ie useless) concatenation of arguments to delay expansion during macro invocation but without success.
i don't know which compiler or IDE u use.
but for visual studio 2012: both line generate same code after preprocessing
int ( ) ;
int ( ) ;
for vs:
'src file' property -> C/C++/Preprocessor -> preprocess to a file
option will output code after preprocessing to a file
GCC have similar compile options.

C++ Macro improperly terminated macro invocation

In C++ is there a mechanism to allow non terminated macros to be expressed?
This is a contrived example:
#define MACRO(x, y) x + y
#define MACROC1(x) MACRO(x,
#define MACROC2(y) y)
//...expecting 3
int foo = MACROC1(1) MACROC2(2);
I receive the error improperly terminated macro invocation from MSVC.
When I run cl -E file.cpp I see that the code below has been generated:
int c = 1 + 1 + 2);
In visual studio compilation fails with errors:
error C2059: syntax error : ')'
IntelliSense: improperly terminated macro invocation
I don't think this is possible. The C precompiler expands macros depth-first, so the MACROC1 will be full expanded before the MACROC2 is even considered. Then, it will find the MACRO with and incomplete argument list and throws an error.
Generally speaking, you should avoid defining macros that build other macro calls. Compilers tend not to agree in what those mean.
Your code would translate to :
int foo = MACRO(1, 2;
Which is wrong - it is an incomplete (improperly terminated) invocation of macro MACRO.