The asterisk is not a character constant? - c++

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.

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"}));

Scons appending a random '1' to macro definition on OSX only

I have a command line argument that defines the type to use for a Vector3 class. It is defined in the SConstruct file as follows:
EnumVariable('vector3_type', 'The type to use for Vector3\'s xyz coordinates', 'float', allowed_values=['float', 'double', 'long_double'], ignorecase=2)
This variable is added to the environment here:
if env['vector3_type'] != 'long_double':
env.Append(CPPDEFINES = ["BIGROCK_VEC3_TYPE %s" % env['vector3_type']])
else:
env.Append(CPPDEFINES = ["BIGROCK_VEC3_TYPE long double"])
This works fine on Windows 10, but when I try to compile on OSX, I get the following error:
In file included from src/octree.cpp:1:
In file included from src/octree.h:5:
src/vector3.h:15:9: error: expected member name or ';' after declaration specifiers
BIGROCK_VEC3_TYPE x, y, z;
^~~~~~~~~~~~~~~~~
<command line>:1:33: note: expanded from here
#define BIGROCK_VEC3_TYPE float 1
~~~~~ ^
Either scons or g++ is adding an extra '1' to the end of my macro, causing the rest of my code to break.
When I print env['vector3_type'] in scons, it doesn't have a 1 attached. I have no idea where this 1 is being added.
This makes me feel a bit foolish. Scons assumes that a lone string within CPPDEFINES is a flag name, and will append 1 to it for #ifdef and #ifndef directives. The proper way to pass this #define is:
if env['vector3_type'] != 'long_double':
env.Append(CPPDEFINES = [("BIGROCK_VEC3_TYPE", env['vector3_type'])])
else:
env.Append(CPPDEFINES = [("BIGROCK_VEC3_TYPE", "long double")])

Why string concat macro doesn't work for this "+" case?

Short question:
Is it permitted to concat special signs such as +, - for the string concatenation macro ##? For example,
#define OP(var) operator##var
will OP(+) be expanded to operator+?
Exact problem:
#include "z3++.h"
#include <unordered_map>
namespace z3 {
z3::expr operator+(z3::expr const &, z3::expr const &);
}
typedef z3::expr (*MyOperatorTy)(z3::expr const &, z3::expr const &);
#define STR(var) #var
#define z3Op(var) static_cast<MyOperatorTy>(&z3::operator##var)
#define StrOpPair(var) \
{ STR(var), z3Op(var) }
void test() {
std::unordered_map<std::string, MyOperatorTy> strOpMap1{
{"+", static_cast<MyOperatorTy>(&z3::operator+)}}; // fine
std::unordered_map<std::string, MyOperatorTy> strOpMap2{StrOpPair(+)}; // error
}
For strOpMap2, using clang++ -c -std=c++11, it reports:
error: pasting formed 'operator+', an invalid preprocessing token
while using g++ -c -std=c++11, it gives:
error: pasting "operator" and "+" does not give a valid preprocessing token
By reading the manual by gcc I find it should be possible to concat, but why both compilers emit errors?
You can paste punctuation to form other punctuation, e.g.
#define PASTE(a,b) a##b
int main()
{
int i = 0;
i PASTE(+,+);
// i == 1 now
}
The ## operator is for producing a valid preprocessing token from other preprocessing tokens. The result of pasting must be a valid preprocessing token. So this is not valid:
PASTE(i,++)
because i++ is not a preprocessing token; it's two adjacent tokens i and ++.
The list of possible tokens is (N3797):
header-name
identifier
pp-number
character-literal
user-defined-character-literal
string-literal
user-defined-string-literal
preprocessing-op-or-punc
each non-white-space character that cannot be one of the above
Note: at the preprocessing stage, keyword does not exist; but after preprocessing, any identifiers which should be keyword are converted (semantically) into keywords. So you can build keywords by pasting shorter words.
In your code, operator+ is two tokens: operator and +. So you do not build it with ##; you just do one then the other.
#define OP(punc) operator punc

Macro expansion within a macro

I'm trying to create LOGDEBUG macro:
#ifdef DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif
#define LOGDEBUG(...) do { if (DEBUG_TEST) syslog(LOG_MAKEPRI(LOG_SYSLOG, LOG_DEBUG), __VA_ARGS__); } while (0)
...
size_t haystack_len = fminl(max_haystack_len, strlen(haystack_start));
LOGDEBUG(("haystack_len %ld\n", haystack_len));
I am not using # or ## parameters to stringify the arguments, and yet g++ apparently tries to stringify them:
numexpr/interpreter.cpp:534:5: error: invalid conversion from ‘size_t {aka long unsigned int}’ to ‘const char*’ [-fpermissive]
Note that haystack_len is size_t and I do not convert it to char* in the macro, yet compiler sees it as such. Does g++ implicitly tries to convert macro arguments to strings?
How to fix that? I mean, I'm using gnu LOG_MAKEPRI macro for syslogging, is it this macro that may be causing trouble? Also, is there some way to see the macro-expanded code?
How to fix that?
LOGDEBUG(("haystack_len %ld\n", haystack_len)); call the macro with one unique argument.
So it will produce:
do { if (DEBUG_TEST) syslog(LOG_MAKEPRI(LOG_SYSLOG, LOG_DEBUG), ("haystack_len %ld\n", haystack_len)); } while (0);
And ("haystack_len %ld\n", haystack_len) use comma operator and result in haystack_len
So you have to call it that way: LOGDEBUG("haystack_len %ld\n", haystack_len);
Also, is there some way to see the macro-expanded code?
gcc -E may help.

Forcing preprocessor error with macro

Is there a way that I can force a preprocessor macro in C++ to emit an error? What I would like to do is define a macro UNKNOWN. I'm writing some code for a robot, and I don't yet know where all of the electronics are being plugged in. I'd like to be able to define the ports in some header file, like
const int MOTOR_PORT = 1;
const int FAN_PORT = 2;
//etc.
However, when I reach a port that I don't yet know, I want to be able to write something like
const int LED_PORT = UNKNOWN;
In debug mode, UNKNOWN would just be defined to some arbitrary value, like 0. However, when compiling in release mode, I want it to throw an error when UNKNOWN is used, so that unassigned ports don't end up in the final release. I know I can use the #error directive to force an error, but can I do something similar in a macro?
I've seen a solution using static_assert, but I unfortunately can't use C++11 for this platform.
Since #error can't result from a macro expansion, you can ensure that the macro expands to something that must be diagnosed, like a syntax error.
For example:
#ifdef RELEASE
#define UNKNOWN #Invalid_use_of_UNKNOWN
#else
#define UNKNOWN 0
#endif
const int MOTOR_PORT = 1;
const int FAN_PORT = 2;
const int LED_PORT = UNKNOWN;
int main(void) {
int x = LED_PORT;
}
The # character isn't part of C's basic character set, so its appearance outside a comment, character constant, or string literal should always result in an error message. ($ would work, except that accepting $ in identifiers is a common extension. ` would probably also work, but # stands out better.)
I've defined the macro so it produces a reasonable error message with gcc:
c.c:9:1: error: stray ‘#’ in program
c.c:9:22: error: ‘Invalid_use_of_UNKNOWN’ undeclared here (not in a function)
and with clang:
c.c:9:22: error: expected expression
const int LED_PORT = UNKNOWN;
^
c.c:2:17: note: expanded from:
#define UNKNOWN #Invalid_use_of_UNKNOWN
^
1 error generated.
(There's a _Pragma operator corresponding to the #pragma directive. It would be nice if there were an _Error operator as well, but there isn't.)
Well, this does not produce a complier error message like #error, but would compile in debug and fail in release:
#ifdef _DEBUG
# define UNKNOWN 1
#else
# define UNKNOWN
#endif
const int port1 = UNKNOWN; // fail in release
You could make a division by zero which will throw a compiler error:
#define UNKNOWN 0/0
The sizeof operator cannot be applied to an incomplete type, so try this:
// Declared, but not defined anywhere.
struct illegal_use_of_unknown_macro;
#define UNKNOWN (sizeof (illegal_use_of_unknown_macro))