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 by0 each time1 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 |
|---|---|
|
|