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"}));
Related
The following code has been compiled with gcc-5.4.0 with no issues:
% gcc -W -Wall a.c
...
#include <stdio.h>
#include <stdarg.h>
static int debug_flag;
static void debug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#define DEBUG(...) \
do { \
if (debug_flag) { \
debug("DEBUG:"__VA_ARGS__); \
} \
} while(0)
int main(void)
{
int dummy = 10;
debug_flag = 1;
DEBUG("debug msg dummy=%d\n", dummy);
return 0;
}
However compiling this with g++ has interesting effects:
% g++ -W -Wall -std=c++11 a.c
a.c: In function ‘int main()’:
a.c:18:10: error: unable to find string literal operator ‘operator""__VA_ARGS__’ with ‘const char [8]’, ‘long unsigned int’ arguments
debug("DEBUG: "__VA_ARGS__); \
% g++ -W -Wall -std=c++0x
<same error>
% g++ -W -Wall -std=c++03
<no errors>
Changing debug("DEBUG:"__VA_ARGS__); to debug("DEBUG:" __VA_ARGS__); i.e. space before __VA_ARGS__ enables to compile with all three -std= options.
What is the reason for such behaviour? Thanks.
Since C++11 there is support for user-defined literals, which are literals, including string literals, immediately (without whitespace) followed by an identifier. A user-defined literal is considered a single preprocessor token. See https://en.cppreference.com/w/cpp/language/user_literal for details on their purpose.
Therefore "DEBUG:"__VA_ARGS__ is a single preprocessor token and it has no special meaning in a macro definition. The correct behavior is to simply place it unchanged into the macro expansion, where it then fails to compile as no user-defined literal operator for a __VA_ARG__ suffix was declared.
So GCC is correct to reject it as C++11 code.
This is one of the backwards-incompatible changes between C++03 and C++11 listed in the appendix of the C++11 standard draft N3337: https://timsong-cpp.github.io/cppwp/n3337/diff.cpp03.lex
Before C++11 the string literal (up to the closing ") would be its own preprocessor token and the following identifier a second preprocessor token, even without whitespace between them.
So GCC is also correct to accept it in C++03 mode. (-std=c++0x is the same as -std=c++11, C++0x was the placeholder name for C++11 when it was still in drafting)
It is also an incompatibility with C (in all revisions up to now) since C doesn't support user-defined literals either and considers the two parts of "DEBUG:"__VA_ARGS__ as two preprocessor tokens as well.
Therefore it is correct for GCC to accept it as C code as well (which is how the gcc command interprets .c files in contrast to g++ which treats them as C++).
To fix this add a whitespace between "DEBUG:" and __VA_ARGS__ as you suggested. That should make it compatible with all C and C++ revisions.
Here is the example code I used to reproduce this error:
#include <iostream>
#define TEST_2_ARG_MACRO_OVERLOAD(_1,_2,FUNC_NAME,...) FUNC_NAME
#define TEST_HELLO_IMPL(condition) do{ \
if(!(condition)) {\
std::cout << "hello!" << std::endl; \
}\
} while(0)
#define TEST_HELLO_MESSAGE_IMPL(condition, message) do{ \
if(!(condition)) {\
std::cout << "hello" << message << std::endl; \
}\
} while(0)
//Runs during runtime and debug
#define TEST_HELLO(...) TEST_2_ARG_MACRO_OVERLOAD(__VA_ARGS__, TEST_HELLO_MESSAGE_IMPL, TEST_HELLO_IMPL)(__VA_ARGS__)
int main()
{
auto x = 3 * (3);
TEST_HELLO(x >= 3);
}
In godbolt with GCC x86-64 8.2 using:
-std=c++14 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wpedantic -Werror
as arguments I get the following error:
<source>:24:22: error: ISO C++11 requires at least one argument for the "..." in a variadic macro [-Werror]
TEST_HELLO(x >= 3);
^
cc1plus: all warnings being treated as errors
Compiler returned: 1
Yet there is clearly at least one argument in the macro. Now I know this compiles when I don't enable this warning, and when I don't enable this warning as an error, but for other reasons I want to keep these compiler flags (or at least keep the same results).
Why does GCC claim that I've passed zero arguments when I have not?
The problem is actually with TEST_2_ARG_MACRO_OVERLOAD, the error message is slightly misleading.
The macro takes 3 arguments and ... , in ISO C++ that means you must pass at least 4 arguments but in fact you only pass 3. (Ref: C++17 [cpp.replace]/4)
The TEST_HELLO expands to TEST_2_ARG_MACRO_OVERLOAD(x >= 3, TEST_HELLO_MESSAGE_IMPL, TEST_HELLO_IMPL)(x >= 3) .
I try to embed a code block through the use of macro like this:
#define RUN_CODE_SNIPPET(c) do {\
c\
} while(0);
where 'c' is a code block enclosed inside '{ }'
Here is how to use it
#include <stdio.h>
#define RUN_CODE_SNIPPET(c) do {\
c\
} while(0);
int main(int argc, char *argv[]) {
RUN_CODE_SNIPPET({
//const char *message = "World";
const char message[] = {'w', 'o', 'r', 'l', 'd', '\0'};
printf("%s\r\n", message);
});
return 0;
}
You can run it here here
But I get compiler error when I use the initializer list format
test.c: In function ‘main’:
test.c:13:4: error: macro "RUN_CODE_SNIPPET" passed 6 arguments, but takes just 1
});
^
test.c:9:3: error: ‘RUN_CODE_SNIPPET’ undeclared (first use in this function)
RUN_CODE_SNIPPET({
^~~~~~~~~~~~~~~~
test.c:9:3: note: each undeclared identifier is reported only once for each
function it appears in
Seems the compiler is taking each element in the initializer list as the argument to the macro itself. The string initializer works fine.
What is wrong here?
The commas in what you pass inside the parentheses are interpreted as macro argument separators and the macro is expecting just one argument.
There are two ways around the problem:
parenthesize the commas-containing argument, i.e., pass (a,b,c) instead of a,b,c (not applicable in your case because your argument is not an expression)
use variadic macro arguments (... -> __VA_ARGS__)
In other words:
#define RUN_CODE_SNIPPET(...) do { __VA_ARGS__; }while(0)
will work (including the semicolon at the end of the macro is not advisable -- for a function-like macro, you should generally be able to do if(X) MACRO(something); else {} and the semicolon would mess that up).
Can somebody explain to me why on Earth does this code snippet refuse to work?
#include <cassert>
#include <type_traits>
using namespace std;
int main()
{
assert(is_same<int, int>::value);
}
Compilation fails because, according to the compiler:
prog.cpp:7:33: error: macro "assert" passed 2 arguments, but takes just 1
assert(is_same<int, int>::value);
^
prog.cpp: In function 'int main()':
prog.cpp:7:2: error: 'assert' was not declared in this scope
assert(is_same<int, int>::value);
^
What? is_same<int, int>::value is undoubtedly one argument. Also assert is declared at this scope, and the compiler itself confirmed it in the previous error!
http://ideone.com/LcMVkn
The macro splits your parameter(s) like this:
is_same<int , int>::value
// ^^ par1 ^^// ^^ par2 ^^
As assert() is a macro definition (with one parameter), it's handled by the C-preprocessor. The preprocessor is unaware of c++ syntax like template parameters gouped in angle brackets (<>) separated with ,. So the parameter expression is split up like shown above.
You can avoid that using extra parenthesis, so the C-preprocessor will take that parameter as a whole:
assert((is_same<int, int>::value));
// ^ ^
I have some code I am maintaining that I've started compiling under clang 3.3.
When compiling with "-std=c++11", clang generates an error (given below). I've distilled the offending code to the following:
#include <stdio.h>
#define DBG_PRT(__format, ...) \
printf("%s:%d:%s: "__format, __FILE__, \
__LINE__, __FUNCTION__, ## __VA_ARGS__)
int main()
{
DBG_PRT("%s\n", "Hi");
}
This is clang's output:
test.cpp:10:5: error: no matching literal operator for call to
'operator "" __format' with arguments of types 'const char *' and
'unsigned int'
DBG_PRT("%s\n", "Hi");
^ test.cpp:4:29: note: expanded from macro 'DBG_PRT'
printf("%s:%d:%s: "__format, __FILE__, \
^ 1 error generated.
Without spaces between the string literal and "__format", it doesn't seem like the preprocessor should be able to expand __format. It clearly is, though, when not specifying -std=c++11. G++ 4.4.7 (with and without -std=c++0x) compiles just fine.
Is there an error with the compiler?
This is because ""_ is a syntax for user-defined string literals. Put a space in between to have the old behavior (concatenate literals). GCC works fine because 4.4.7 does not implement user defined literals (it appeared in version 4.7).
Also, as #Fred have pointed out, try to avoid using reserved identifier (double underscore).