Format enums annotated with fmt::as_identifiers (requires C++26 reflection) - #4885
Conversation
fc54dab to
24d2aaa
Compare
|
v2: apply cmake-format |
|
@vitaut please review |
vitaut
left a comment
There was a problem hiding this comment.
This looks great, very cool use of C++26 annotations for enum formatting. Thanks for putting this together!
One thing I noticed: it looks like the reflection tests are currently skipped everywhere because none of the CI compilers support reflection. It would be good to have at least one GCC 16 + -freflection job that actually builds and runs this code.
24d2aaa to
8a7231d
Compare
|
Update:
|
Done. |
8a7231d to
1fa7a9a
Compare
|
Update: support C++20 modules too |
vitaut
left a comment
There was a problem hiding this comment.
Mostly looks good but I think unknown values of enum : char will be formatted as char rather than decimal and also defined_static_string seems unnecessary.
| * A value that doesn't match any enumerator is formatted as the corresponding | ||
| * value of the underlying type. | ||
| */ | ||
| inline constexpr auto as_identifiers = as_identifiers_t(); |
There was a problem hiding this comment.
I wonder if this should be singular as_identifier because the result is a single identifier?
There was a problem hiding this comment.
I read it as "format this enum type as its identifiers" rather than "format this enum value as the corresponding identifier", since the type is annotated.
| for (std::meta::info e : std::meta::enumerators_of(^^E)) { | ||
| auto id = std::meta::identifier_of(e); | ||
| ids[i++] = {std::meta::extract<E>(e), | ||
| string_view(std::define_static_string(id), id.size())}; |
There was a problem hiding this comment.
I don't think define_static_string is necessary. identifier_of should already provide the required lifetime guarantee.
…ction)
Format an enum as the identifier of the matching enumerator if the enum is
annotated with fmt::as_identifiers:
enum class [[=fmt::as_identifiers]] color { red, green, blue };
fmt::format("{}", color::green); // "green"
Values that don't match any enumerator are represented as their underlying
value in decimal before applying string formatting.
Identifiers are retrieved via C++26 reflection (P2996) and the annotation
via P3394. FMT_USE_REFLECTION is autodetected and can be overridden by the
user; without reflection the header is empty.
The header is also part of the fmt module, but, unlike with headers, whether
it provides anything is decided when the module is compiled, so the module
build detects reflection and enables it if the configured standard allows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Good catch, fixed.
Yes |
The formatter for enums annotated with fmt::as_identifiers did a linear search over all enumerators. Build a table indexed by the distance from the smallest enumerator value instead, with empty string views in the holes, which reduces the lookup to a bounds check and one load. The table is only used if at least 70% of its elements are identifiers, limiting its size to 10/7 of the number of enumerators. Sparser enums keep using the linear search. Distances are computed in uint64_t so that enums with negative values and values spanning the whole range of the underlying type are handled without overflow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
GCC 16 warns when a type is completed after it failed to be complete in a SFINAE context. format-test does this deliberately to check that formatting of incomplete types works, so the warning is a false positive there and breaks the build with -Werror.
GCC 16 is the first compiler with C++26 reflection support, which is needed by fmt/enum.h, so add a job that builds with it in C++26 mode. It comes from the ubuntu-toolchain-r/test PPA since Ubuntu 24.04 only ships GCC 14. Also report when reflection is not detected to make it visible that enum-test was skipped.
1fa7a9a to
2f48cd0
Compare
|
Update:
Unchanged:
|
|
Merged, thanks! |
Format an enum as the identifier of the matching enumerator if the enum is
annotated with fmt::as_identifiers:
enum class [[=fmt::as_identifiers]] color { red, green, blue };
fmt::format("{}", color::green); // "green"
Values that don't match any enumerator are represented as their underlying
value in decimal before applying string formatting.
Identifiers are retrieved via C++26 reflection (P2996) and the annotation
via P3394. FMT_USE_REFLECTION is autodetected and can be overridden by the
user; without reflection the header is empty.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com