Using #define inside initializer list - c++

Recently I came across a piece of code similar to this one here
std::map<size_t,std::string> map{
#define RT_OK 0
{RT_OK,"No Error"},
#define RT_SIZE_MISMATCH 1
{RT_SIZE_MISMATCH,"Size Mismatch"}
};
using #define just inside the initializer list.
I was actually surprised that it worked with GCC and it seems to work with CLANG too. Anyway is it ok to use #define inside an initializer list?

It's "OK"1 to put macro definitions anywhere2.
Pre-processor directives are removed by the pre-processor. The compiler sees something like:
std::map<size_t,std::string> map{
// there was a PP directive here
{0,"No Error"},
// there was a PP directive here
{1,"Size Mismatch"}
};
1 In the sense that the program is well-formed. It may sometimes be not OK because it may be confusing to other programmers.
2 Restrictions apply. There must not be any non-whitespace tokens on the same line prior to the directive, and the directive continues until the end of the line.

Related

Condition inclusion through has-include-expression and has-attribute-expression behaves differently

#include <iostream>
#define Abc likely
# if __has_cpp_attribute(Abc)
#define Pn 0
#endif
#if __has_cpp_attribute(likely)
#ifndef Pn
#define Pn 1
#endif
#endif
int main(){
std::cout<< Pn;
}
For this example, GCC prints 0 while Clang prints 1. According to [cpp.cond] p5
Each has-attribute-expression is replaced by a non-zero pp-number matching the form of an integer-literal if the implementation supports an attribute with the name specified by interpreting the pp-tokens, after macro expansion, as an attribute-token, and by 0 otherwise. The program is ill-formed if the pp-tokens do not match the form of an attribute-token.
So, the directive # if __has_cpp_attribute(Abc) should behave the same as #if __has_cpp_attribute(likely). GCC has the right behavior. Again, consider this example
#include <iostream>
#define Head <iostream>
# if __has_include(Head)
#define Pn 0
#endif
#ifndef Pn
#define Pn 1
#endif
int main(){
std::cout<< Pn;
}
In this example, both compilers print 0. However, according to [cpp.cond] p4
The header or source file identified by the parenthesized preprocessing token sequence in each contained has-include-expression is searched for as if that preprocessing token sequence were the pp-tokens in a #include directive, except that no further macro expansion is performed. If such a directive would not satisfy the syntactic requirements of a #include directive, the program is ill-formed. The has-include-expression evaluates to 1 if the search for the source file succeeds, and to 0 if the search fails.
Note the bold wording, which means Head won't be replaced by <iostream>, there is no such a source file. Hence, Pn should be 1 instead. Could it be considered a bug of GCC and Clang?
I am not sure that the answer below is correct. I will leave it up for reference for now.
I think the second example does not fit the has-include-expression grammar. If you look at [cpp.cond] there are two forms mentioned, which are further subdivided into multiple cases, referring also to [lex.header].
Collecting the possible forms and combining them here for presentation, we get:
__has_include(<...>)
__has_include("...")
__has_include(string-literal)
with ... as some placeholder and string-literal any string literal. Your form __has_include(Head) is none of these, since Head neither starts with ", nor <, nor is it a string literal.
[cpp.cond]/3 does mention that if the first of the two syntax choices for has-include-expression does not match, the second is considered and the preprocessor tokens are processed like normal text, presumably meaning they are macro-expanded. However it is not clear to me whether this is supposed to reference all preprocessor tokens between ( and ) before the above-mentioned grammar rules are applied or just the h-pp-tokens in the __has_include(<h-pp-tokens>) form. In the former case, the compilers would be correct in returning 0.
However, the latter case makes more sense to me, especially when comparing e.g. to the grammar rule for #include, which uses similar forms, but instead of #include <h-pp-tokens> the last form is #include pp-tokens. [cpp.include]
[cpp.cond]/7 says that the identifier __has_include shall not appear in any context not mentioned in the subclause. I would think that shall not here means otherwise ill-formed, in which case the program should not compile without diagnostic. If it means otherwise undefined behavior, then all compilers are correct.
For the first example, I think you are right. Clang has a recently-fixed bug report regarding the macro expansion here and if you choose Clang trunk on compiler explorer, the result will coincide with GCC already now.

Is function-like macro argument name substitution guaranteed to never happen?

Consider the following code
#define foo 38
#define F(foo) G(foo)
F(42);
I expect this code to fail to compile because after applying the first line second line should transform into #define F(38) G(38) which does not make any sense. But on g++ it successfully compiles into G(42), as if there was no first line at all. I was unable to find any mention of this behaviour in neither g++ docs nor c standard. I know that code is ugly and should not be used in the first place, but I wonder if it has any guarantees to be portable.
C 2018 6.10 7 and C++ 2017 (draft n4659) 19 [cpp] 6 both say:
The preprocessing tokens within a preprocessing directive are not subject to macro expansion unless otherwise stated.
For parameters of function-like macros, nothing is otherwise stated. Therefore, the parameter names in the definition of a function-like macro are not replaced due to prior macro definitions.
(Examples of where macro expansion is otherwise stated include #if directives and #include directives.)

A macro defined with brackets and without brackets - redefinition error

What is the difference between #define WITHBRACKETS (1) and #define WITHBRACKETS 1?
I have macros defined in two places with same name (I know it's a bad thing that likely results in a redefinition warning) but they are defined differently.
So when I compile the code base, why does the compiler say, #define WITHBRACKETS (1) is an incompatible redefinition of #define WITHBRACKETS 1?
The preprocessor complains it's an incompatible redefinition because it is!
A macro is a token that the preprocessor replaces by a sequence of 0 or more other tokens whenever it encounters it.
#define WITHBRACKETS 1 Will define a token that will be replaced by a single other token.
#define WITHBRACKETS (1) Will define a token to be replaced by three other tokens.
Those aren't compatible things. The preprocessor doesn't know or care that their semantic meaning in the source is the same. All it cares about is the sequence of tokens.
It's a good idea to add the brackets sometimes, for example with something like this :
#define EXAMPLE (1 + 2)
the brackets here can be useful against operator priority errors ...
In your example, the compiler says there is a redefinition because the preprocessor does not know that 1 is the same as (1), it just sees that there are two different sequences of characters.
In simple words: Macros are merely about textual replacement.
If you have those two macros:
#define WITH (1)
#define WITHOUT 1
Then this:
foo( WITHOUT );
foo WITH ;
will expand to
foo( 1 );
foo (1);
So the two defines are indeed different. You can use the -E flag on gcc to see the output after the preprocessing.
PS: Dont use macros (if you dont need to ;).

Finnicky #define behaviour in c++ [duplicate]

I am trying to compare to a defined constants in C, and I have simplified my program to the following:
#include "stdio.h"
#include "stdlib.h"
#define INVALID_VALUE -999;
int main(void)
{
int test=0;
if(test==INVALID_VALUE) //The error line..
return INVALID_VALUE;
return 0;
}
And when I use gcc to compile, it gives out error "error: expected ‘)’ before ‘;’ token".
Is there any reason that this cannot be done?
Remove the semicolon from your INVALID_VALUE definition.
Macros are replaced lexically (character-by-character) with no understanding of the syntax around them. Your macro INVALID_VALUE is set to -999;, so your if line expands the macro to:
if (test==-999;)
which is invalid C syntax.
You need to remove the ; in #define INVALID_VALUE -999;. See the fixed code.
You could have worked towards this conclusion by understanding what the error message expected ‘)’ before ‘;’ token was telling you. It's telling you that it expected to find a ) before the ; token, but from inspecting the line alone you don't see a ;. So maybe there's one in the definition of INVALID_VALUE? Look up at #define INVALID_VALUE -999; and there it is! Think it should be there, but not sure? So let's try remove it and see if it works. Success!
This page goes and explains why you shouldn't conclude a #define with a semicolon, even if it is needed in the use of the macro. It's good to learn as much as possible from your mistake so that you don't make it again. Quote:
Macro definitions, regardless of
whether they expand to a single or
multiple statements should not
conclude with a semicolon. If
required, the semicolon should be
included following the macro
expansion. Inadvertently inserting a
semicolon at the end of the macro
definition can unexpectedly change the
control flow of the program.
Another way to avoid this problem is
to prefer inline or static functions
over function-like macros.
In general, the programmer should
ensure that there is no semicolon at
the end of a macro definition. The
responsibility for having a semicolon
where needed during the use of such a
macro should be delegated to the
person invoking the macro.
The C Preprocessor Macro Language is Distinct from C
The ; in the macro definition should be removed.
This is an understandable mistake. If C were designed today, the macro language might be more integrated with the rest of C.
But on 16-bit machines in the early 1970's when C was invented, it was unwise to write an overly complicated program. It would end up useless as there would be no memory remaining to actually run the big masterpiece program, and even simple programs ran slowly.
So C was split into a rather simple macro preprocessor that was, originally, a completely separate program, and the compiler proper. The preprocessor program made no attempt to parse C beyond understanding the lexical analysis model.
When 32-bit machines took over, the preprocessor was typically integrated into the parser, but naturally the language needed to remain the same.
The semi colon at the end of
#define INVALID_VALUE -999;
Classic.
You do not need a semicolon after defining something. #define is actually a macro, and it will do an inline expansion on compile.
Thus,
#define IDENTIFIER 10;
int j = IDENTIFIER;
will expand as:
int j = 10;;
Change macro
from:
#define INVALID_VALUE -999;
to
#define INVALID_VALUE -999
Bye

#ifdef inside a macro call works with gcc but not with msvc [duplicate]

This question already has an answer here:
Are preprocessor directives allowed in a function-like macro's argument?
(1 answer)
Closed 5 months ago.
I am have a macro TYPELIST which takes variadic arguments. I want to have something like
typedef TYPELIST(A
,B
,C
,D
#ifdef BLA_
,E
#endif
,F)
This works perfectly with gcc. However, when I try to compile it with MSVC it parses ifdef and endif as macro arguments. I know one way would be to put the macro call inside an ifdef. However, if I have a huge list and if I want to include different classes depending on different macros defined, it would become tedious. Is there a particular reason why this works in gcc and not with MSVC?
Using #ifdef inside a macro isn't legal. I am sort of surprised that gcc allows this. I'm afraid you have to put the #ifdef around the entire definition, i.e.
#ifdef BLA_
typedef TYPELIST(a,b,c,d,e,f)
#else
typedef TYPELIST(a,b,c,d,f)
#endif
According to the standard (§16.3.4/3), "The resulting completely
macro-replaced preprocessing token sequence is not processed as
a preprocessing directive even if it resembles one,[...]". If
g++ processes the #ifdef/#endif here, it's an error in the
compiler (at least if you've requested standards conformance,
e.g. with -std=...).