1. Introduction
The
pre-defined macro is a language extension for C and C++ which expands to an integer literal that
starts at
and increments by
every time it expanded in a translation unit. This is a widely-used utility that
is primarily useful for generating unique identifiers with preprocessor macros.
2. Motivating Examples
A brief survey of some uses of
in the C and C++ community:
-
Google benchmark uses
for unique identifiers, falling back to__COUNTER__
if__LINE__
isn’t present or doesn’t behave as expected__COUNTER__ -
Google Orbit uses
for unique identifiers__COUNTER__ -
LLVM uses
for unique identifiers as well as in sanitizer code to prevent ICF__COUNTER__ -
Catch2 uses
for unique identifiers, falling back to__COUNTER__ __LINE__ -
Tensorflow uses
extensively, primarily for unique identifiers__COUNTER__ -
Chromium uses
for unique identifier generation, e.g. in crash logging code, as well as for creating unique tags for__COUNTER__
sABORT () -
Folly uses
for unique identifiers, falling back to__COUNTER__
if not present__LINE__ -
v8 uses
for unique identifiers__COUNTER__ -
Metric Panda Games uses
for lookup tables as part of a localization and compile-time string hashing system.__COUNTER__
3. Implementation Support
has long been supported by all major implementations of C and C++:
Compiler | Earliest Version Tested | Earliest Version Tested Supporting
|
---|---|---|
GCC | 3.4.6 ❌ | 4.4.7 ✔️ |
Clang | 3.0.0 ✔️ | 3.0.0 ✔️ |
MSVC | 19.0 ✔️ | 19.0 ✔️ |
ICC | 13.0.1 ✔️ | 13.0.1 ✔️ |
ICX | 2021.1.2 ✔️ | 2021.1.2 ✔️ |
EDG | 6.5 ✔️* | 6.5 ✔️* |
TCC | 0.9.27 ✔️ | 0.9.27 ✔️ |
Movfuscator | Trunk ✔️ | Trunk ✔️ |
*: Supported only in
mode
Comparison: https://godbolt.org/z/vM7xsPE3M
4. Design Considerations
4.1. Precompiled Headers
MSVC and GCC save the state of
in precompiled headers. GCC notes that the
macro must not be
expanded prior to inclusion of a pre-compiled header. If it is, then the precompiled header is not used.
This paper proposes no change to the current behavior.
4.2. Modules
GCC and MSVC do not propagate
across modules, including for header units. The following compiles with a
linker error due to multiple definitions of
:
// header.hpp #define CONCAT_IMPL(x, y) x##y #define CONCAT(x, y) CONCAT_IMPL(x, y) #define NEW_VAR(name) CONCAT(name, __COUNTER__) int NEW_VAR ( x ); // x0 int NEW_VAR ( x ); // x1 // main.cpp import "header.hpp" int NEW_VAR ( x ); // x0
There are similar concerns with
and
macros surrounding header units, though the potential for
problems is less pronounced. One option would to disallow the expansion of
in header units, however, no
such restriction is proposed in this proposal.
This paper proposes no change to the current behavior. Other behaviors would introduce additional complexity without clear benefit.
4.3. ODR
It’s possible to accidentally violate ODR with
:
// foo.hpp #define CONCAT_IMPL(x, y) x##y #define CONCAT(x, y) CONCAT_IMPL(x, y) #define NEW_VAR(name) CONCAT(name, __COUNTER__) inline void foo () { int NEW_VAR ( x ) = 2 ; } // a.cpp #include "foo.hpp"// b.cpp int x = __COUNTER__ ; #include "foo.hpp"
Current implementations do not make any special attempt to diagnose or prevent such use of
beyond existing
ODR diagnostics. Similar ODR issues can occur as a result of
and
.
4.4. Range and Overflow
is implemented with an
counter in GCC and Clang and both implementations wrap around to zero
when that counter overflows. This paper recommends
shall be able to attain a value of at
least 232 - 1 with an overflow behavior of wrapping to zero.
5. Proposed Wording
Proposed wording relative to [N4950]:
Insert a bullet point in [cpp.predefined/1] before bullet 1:
__COUNTER__
An integer literal that starts atand increments by
0 each time
1 is expanded.
__COUNTER__ shall have a maximum value of at least 232 - 1. If the value of
__COUNTER__ exceeds its implementation-defined maximum value it shall reset to zero.
__COUNTER__
Insert a row into Table 22 in [cpp.predefined/1] after the row for
:
Macro name | Value |
---|---|
|
|