PnnnnR0
__COUNTER__

New Proposal,

This version:
https://jeremy-rifkin.github.io/cpp-proposals/drafts/counter_initial_draft.html
Author:
Jeremy Rifkin
Project:
ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
Source:
https://github.com/jeremy-rifkin/cpp-proposals/blob/main/src/counter.bs

Abstract

__COUNTER__ is a widely-used predefined macro provided as a language extension by all major C and C++ implementations. This paper aims to standardize existing practices.

1. Introduction

The __COUNTER__ pre-defined macro is a language extension for C and C++ which expands to an integer literal that starts at 0 and increments by 1 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 __COUNTER__ in the C and C++ community:

3. Implementation Support

__COUNTER__ has long been supported by all major implementations of C and C++:

Compiler Earliest Version Tested Earliest Version Tested
Supporting __COUNTER__
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 --microsoft mode

Comparison: https://godbolt.org/z/vM7xsPE3M

4. Design Considerations

4.1. Precompiled Headers

MSVC and GCC save the state of __COUNTER__ in precompiled headers. GCC notes that the __COUNTER__ 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 __COUNTER__ across modules, including for header units. The following compiles with a linker error due to multiple definitions of x0:

// 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 __TIME__ and __DATE__ macros surrounding header units, though the potential for problems is less pronounced. One option would to disallow the expansion of __COUNTER__ 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 __COUNTER__:

// 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 __COUNTER__ beyond existing ODR diagnostics. Similar ODR issues can occur as a result of __DATE__ and __TIME__.

4.4. Range and Overflow

__COUNTER__ is implemented with an unsigned counter in GCC and Clang and both implementations wrap around to zero when that counter overflows. This paper recommends __COUNTER__ 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:

Insert a row into Table 22 in [cpp.predefined/1] after the row for __cpp_constinit:

Macro name Value
__cpp_counter 20XXXXL

References

Normative References

[N4950]
Thomas Köppe. Working Draft, Standard for Programming Language C++. 10 May 2023. URL: https://wg21.link/n4950