Issue
I have a header that is shared between C and C++ with some C++ specific code in it. So i use #ifdef __cplusplus
inside it.
However, I have a value defined with an integer literal and a single quote as a separator: int a = 0B0000'0001;
, and it looks like the preprocessor does not like it.
It`s working correctly with visual studio, but not linux, both with gcc and unifdef (that helped me understand the error)
#ifdef __cplusplus
int a = 0B0000'0001;
#endif
This simple code will break C code.
The easy solution would be to remove the single quote. But if there is another solution, it would be nice to know
EDIT: the duplicate kinda answer the question, but it's not the same since I dont actually need to use the digit separator in C (but the preprocessor does)
So, to sum up:
- if you have gcc >=12: use flag -std=c2x
- if you dont, you can use a workaround:
// workaround: final quote for pre-c2x: '
on the same line
Solution
Transferring commentary into an answer and expanding on the discussion.
C++14 was the first standard to include numeric punctuation using '
to separate groups of digits. There are technical reasons why C++ could not use _
instead of '
. C++14 also added binary constants, prefixed by 0b
or 0B
.
The numeric punctuation and binary constants features will be added to C23. This brings it into line with C++, but the numeric punctuation makes ad hoc parsing of C code much harder than it was beforehand.
It means you can compile the code with a version of GCC that recognizes -std=c2x
(or -std=c23
or -std=c24
— I'm not sure which they'll decide on).
You can't compile the code with a C compiler that only recognizes C18 (or is that C17?) or an earlier standard. That's because the input is tokenized by the C preprocessor.
The C11 standard §6.10.1 Conditional inclusion ¶6 specifies:
Each directive's condition is checked in order. If it evaluates to false (zero), the group that it controls is skipped: directives are processed only through the name that determines the directive in order to keep track of the level of nested conditionals; the rest of the directives' preprocessing tokens are ignored, as are the other preprocessing tokens in the group. Only the first group whose control condition evaluates to true (nonzero) is processed. If none of the conditions evaluates to true, and there is a
#else
directive, the group controlled by the#else
is processed; lacking a#else
directive, all the groups until the#endif
are skipped.
The problem is that it works on "preprocessing tokens", and prior to C23, the '
in 0B0000'0001
starts a character constant token, but there is no matching close quote, so the token is invalid/incomplete.
There are a few possible but nasty, hacky workarounds. One, suggested by Jarod42 in a comment, is:
Adding
// workaround: final quote for pre-c2x: '
might be a workaround.
In a similar vein, you could ensure that all punctuated numbers contain an even number of single quotes. For example, punctuating the binary in octal digit groupings would work too: 0B00'000'001
. Always use an even number of punctuation characters!
You can also put the C++ stuff in a different file and have
#ifdef __cplusplus #include "./cpp_header.hpp" #endif
so the C preprocessor doesn't even open the file which has
0B0000'0001
in it.
Note that it isn't very sensible to represent 64-bit numbers in binary, even with punctuation. And if you punctuate in octets, that will have 7 punctuators too!
0B10100000'00000100'00000000'00000010'00000000'00101010'00000000'00111000
It would be more sensible in hex, but there would be three separators between four quartets of hex digits, still causing trouble with compilers for C18 or earlier and C++11 or earlier:
0xA004'0002'002A'0038
My recommendation would be:
- Don't use numeric punctuation until all the C and C++ compilers you need to work with support it.
- Don't use binary to represent 64-bit values. Use hex instead.
(I'll note in passing that C++ raw string literals are even more of a nightmare for ad hoc code parsers. Mercifully, raw string literals are not a part of C23.)
Answered By - Jonathan Leffler Answer Checked By - David Marino (WPSolving Volunteer)