On 21/04/22 04:31 -0700, Kaz Kylheku wrote:
libcpp/ChangeLog
2022-04-21 Kaz Kylheku <k...@kylheku.com>
This change introduces a pair of related macros
__EXP_COUNTER__ and __UEXP_COUNTER__. These macros access
integer values which enumerate macro expansions.
They can be used for the purposes of obtaining, unique
identifiers (within the scope of a translation unit), as a
replacement for unreliable hacks based on __LINE__.
Outside of macro expansions, these macros expand to 1,
so they are easy to test for in portable code that needs
to fall back on something, like __LINE__.
* gcc/doc/cpp.texi (__EXP_COUNTER__, __UEXP_COUNTER__):
Special built-in macros documented.
* libcpp/include/cpplib.h (struct cpp_macro): New members of
type long long: exp_number and uexp_number. These members are
used to attach the current exp_counter value, and the parent
context's copy of it to a new macro expansion. The special
macros then just access these.
(enum cpp_builtin_type): New enumeration members,
BT_EXP_COUNTER and BT_UEXP_COUNTER.
* libcpp/macro.cc (exp_counter): New static variable for
counting expansions. There is an existing one,
num_expanded_macros_counter, but that has its own purpose and
is incremented in a specific place. Plus it uses a narrower
integer type.
(_cpp_builtin_number_text): Change the local variable "number"
from linenum_type to unsigned long long, so it holds at least
64 bit values. Handle the BT_EXP_COUNTER and BT_UEXP_COUNTER
cases. These just have to see if there is a current macro, and
retrieve the values from it, otherwise do nothing so that the
default 1 is produced. In the case of BT_UEXP_COUNTER, if the
value is zero, we don't use it, so 1 emerges. The sprintf of
the number is adjusted to use the right conversion specifier
for the wider type. Space is already being reserved for
a 64 bit decimal.
(enter_macro_context): After processing the macro arguments,
if any, we increment exp_counter and attach its new value to
the macro's context structure in the exp_number member. We
also calculate the macro's uexp_number: the parent context's
exp_number. This is tricky: we have to chase the previous
macro context. This works if the macro is object-like, or has
no parameters. If it has parameters, there is a parameter
context, and so we have to climb one more flight of stairs to
get to the real context.
gcc/testsuite/ChangeLog
2022-04-21 Kaz Kylheku <k...@kylheku.com>
* gcc.dg/cpp/expcounter1.c: New test.
* gcc.dg/cpp/expcounter2.c: New test.
* gcc.dg/cpp/expcounter3.c: New test.
* gcc.dg/cpp/expcounter4.c: New test.
* gcc.dg/cpp/expcounter5.c: New test.
Signed-off-by: Kaz Kylheku <k...@kylheku.com>
---
gcc/doc/cpp.texi | 81 ++++++++++++++++++++++++++
gcc/testsuite/ChangeLog | 8 +++
gcc/testsuite/gcc.dg/cpp/expcounter1.c | 16 +++++
gcc/testsuite/gcc.dg/cpp/expcounter2.c | 21 +++++++
gcc/testsuite/gcc.dg/cpp/expcounter3.c | 22 +++++++
gcc/testsuite/gcc.dg/cpp/expcounter4.c | 22 +++++++
gcc/testsuite/gcc.dg/cpp/expcounter5.c | 28 +++++++++
libcpp/ChangeLog | 49 ++++++++++++++++
libcpp/include/cpplib.h | 8 +++
libcpp/init.cc | 2 +
libcpp/macro.cc | 44 +++++++++++++-
11 files changed, 299 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter1.c
create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter2.c
create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter3.c
create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter4.c
create mode 100644 gcc/testsuite/gcc.dg/cpp/expcounter5.c
diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index 90b2767e39a..d52450958d7 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -1941,6 +1941,87 @@ generate unique identifiers. Care must be
taken to ensure that
@code{__COUNTER__} is not expanded prior to inclusion of precompiled headers
which use it. Otherwise, the precompiled headers will not be used.
+@item __EXP_COUNTER__
+This macro's name means "(macro) expansion counter".
+Outside of macro replacement sequences, it expands to the integer
+token @code{1}. This make it possible to easily test for the presence
+of this feature using conditional directives such as
+@code{#if __EXP_COUNTER__}.
It's a macro, so you can just use '#ifdef __EXP_COUNTER__' to test if
it's supported. Is this additional behaviour necessary?
+When @code{__EXP_COUNTER__} occurs in the replacement token sequence
+of a macro, it expands to positive decimal integer token which
expands to _a_ positive decimal integer token?
+uniquely identifies the expansion, within a translation unit.
+Unlike @code{__COUNTER__}, @code{__EXP_COUNTER__} does not increment
+on each access: all references to it which occur in the same token
+replacement sequence of the same instance of a macro being expanded
+produce the same integer token.
+
+The implementation of this feature works as follows: a counter is
initialized
+to zero prior to the processing of any tokens of the translation
unit. Whenever
+a macro is about to be expanded, the counter is incremented, and the
counter's
+value is associated with that macro expansion context. Subsequent
increments of
+the counter, such as during rescanning of a token sequence for more
macros, do
+not affect the captured value. Nested macro expansions are associated with
+their own @code{__EXP_COUNTER__} values. The counter uses the
+@code{unsigned long long} type of the host implementation for which the
+preprocessor is compiled. If this counter overflows and
@code{__EXP_COUNTER__}
+is subsequently accessed, the behavior is unspecified.
+
+The main use for @code{__EXP_COUNTER__} is the generation of unique symbols,
+as in the following example:
+
+@smallexample
+#define cat(a, b) a ## b
+#define xcat(a, b) cat (a, b)
+#define uni(pfx, count) xcat (pfx, xcat (_uniq_, count))
+
+#define repeat(count) \
+ for (int uni (i, __EXP_COUNTER__) = 0, \
+ uni (c, __EXP_COUNTER__) = (count); \
+ uni (i, __EXP_COUNTER__) < uni (c, __EXP_COUNTER__); \
+ uni (i, __EXP_COUNTER__)++)
+
+repeat (get_repeat_count ())
+ puts ("Hello, world!")
+@end smallexample
+
+@item __UEXP_COUNTER__
+This macro is closely related to @code{__EXP_COUNTER__}. In the
+expansion of a macro which is being expanded in the middle of another
+macro expansion, @code{__UEXP_COUNTER__} ("upper context expansion counter")
+produces the @code{__EXP_COUNTER__} value of the parent expansion.
+In any other situation, this macro expands to @code{1}.
+
+Consider the following example:
+
+@smallexample
+#define child __UEXP_COUNTER__ __EXP_COUNTER__
+#define parent @{ __EXP_COUNTER__ child @}
+parent
+@end smallexample
+
+Here, @code{parent} will expand to a sequence similar to @code{@{ 42
42 43 @}}.
+The @code{__UEXP_COUNTER__} value from @code{child} matches the
+@code{__EXP_COUNTER__} in @code{parent}, but the @code{child}'s own
+@code{__EXP_COUNTER__} is, of course, different.
+
+The main use for @code{__UEXP_COUNTER__} is the simplification of macros
+that require @code{__EXP_COUNTER__}. The example given for
+@code{__EXP_COUNTER__} can be simplified like this:
+
+@smallexample
+#define cat(a, b) a ## b
+#define xcat(a, b) cat (a, b)
+#define uni(pfx) xcat (pfx, xcat (_uniq_, __UEXP_COUNTER__))
+
+#define repeat(count) \
+ for (int uni (i) = 0, uni (c) = (count); \
+ uni (i) < uni (c); \
+ uni (i)++)
+
+repeat (get_repeat_count ())
+ puts ("Hello, world!")
+@end smallexample
+
@item __GFORTRAN__
The GNU Fortran compiler defines this.
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e147f69c8eb..fee0ae0ad4b 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2022-04-21 Kaz Kylheku <k...@kylheku.com>
+
+ * gcc.dg/cpp/expcounter1.c: New test.
+ * gcc.dg/cpp/expcounter2.c: New test.
+ * gcc.dg/cpp/expcounter3.c: New test.
+ * gcc.dg/cpp/expcounter4.c: New test.
+ * gcc.dg/cpp/expcounter5.c: New test.
+
2022-04-15 Paul A. Clarke <p...@us.ibm.com>
* g++.dg/debug/dwarf2/const2.C: Move to g++.target/powerpc.
ChangeLog files are autogenerated every night, please don't include
modifications to them in patch submissions (it's not necessary, and it
means the patch won't apply cleanly because the ChangeLog will have
moved on since you created the patch).
I don't have an opinion on the implementation, or the proposal itself,
except that the implementation seems susprisingly simple, which is
nice.