diff --git a/doc/api.md b/doc/api.md
index fb39083e..a8e8da09 100644
--- a/doc/api.md
+++ b/doc/api.md
@@ -591,20 +591,19 @@ Enums without the annotation are not affected and are formatted as before, i.e.
scoped enums require `format_as` or a `formatter` specialization, see
[Formatting User-Defined Types](#udt).
-Identifiers are retrieved with C++26 reflection ([P2996](
-https://wg21.link/p2996)) and the annotation with [P3394](
-https://wg21.link/p3394), so this requires a compiler with reflection support,
+This uses two C++26 features:
+[reflection](https://en.cppreference.com/w/cpp/language/operator_reflection) to
+retrieve the enumerator identifiers and
+[annotations](https://en.cppreference.com/w/cpp/language/annotations) to opt an
+enum in via `fmt::as_identifiers`. It therefore requires a compiler with
+reflection support,
which may need an extra flag such as `-freflection` in GCC. The macro
`FMT_USE_REFLECTION` is set to 1 if reflection is available and to 0 otherwise.
-It can also be defined by the user to disable the use of reflection, in which
-case `fmt/enum.h` is empty.
+It can also be defined by the user to disable the use of reflection.
When {fmt} is built as a module, reflection support is detected when the module
itself is compiled, so this API is only available to importers if the module was
-built with reflection enabled. An importing translation unit may also have to
-include `` itself: some compilers, such as GCC 16, fail to look up
-implementation details of `std::define_static_string` when instantiating the
-formatter otherwise.
+built with reflection enabled.
## Compile-Time Support
diff --git a/include/fmt/enum.h b/include/fmt/enum.h
index bbe1a18f..ad863a7f 100644
--- a/include/fmt/enum.h
+++ b/include/fmt/enum.h
@@ -25,6 +25,7 @@
#if FMT_USE_REFLECTION && !defined(FMT_MODULE)
# include
# include
+# include
# include // std::pair
#endif
@@ -61,13 +62,6 @@ consteval auto use_identifiers() -> bool {
}
}
-// Returns the identifier of e. identifier_of returns a view of a string with
-// static storage duration so it doesn't need to be copied.
-inline consteval auto identifier(std::meta::info e) -> string_view {
- auto id = std::meta::identifier_of(e);
- return string_view(id.data(), id.size());
-}
-
// Returns the underlying value of `value` converted to uint64_t. Negative
// values wrap around, so the difference of two such values is the distance
// between them.
@@ -75,10 +69,6 @@ template constexpr auto to_uint64(E value) -> uint64_t {
return static_cast(static_cast>(value));
}
-template consteval auto count_enumerators() -> size_t {
- return std::meta::enumerators_of(^^E).size();
-}
-
// Returns the smallest enumerator value of E or 0 if E has no enumerators.
template consteval auto min_enumerator() -> E {
auto enumerators = std::meta::enumerators_of(^^E);
@@ -108,13 +98,15 @@ template consteval auto identifier_table_size() -> size_t {
}
template ()>
-consteval auto make_identifier_table() -> std::array {
- auto ids = std::array();
+consteval auto make_identifier_table() -> std::array {
+ auto ids = std::array();
auto min = to_uint64(min_enumerator());
for (std::meta::info e : std::meta::enumerators_of(^^E)) {
auto i = static_cast(to_uint64(std::meta::extract(e)) - min);
- // Keep the identifier of the first enumerator with this value.
- if (ids[i].size() == 0) ids[i] = identifier(e);
+ // identifier_of returns a view of a string with static storage duration so
+ // it doesn't need to be copied. Keep the identifier of the first
+ // enumerator with this value.
+ if (ids[i].size() == 0) ids[i] = std::meta::identifier_of(e);
}
return ids;
}
@@ -129,7 +121,7 @@ inline constexpr auto identifier_table = make_identifier_table();
// below 0.5, which guarantees a free slot and therefore terminates the search.
template consteval auto identifier_map_size() -> size_t {
auto size = size_t(1);
- while (size < count_enumerators() * 2) size *= 2;
+ while (size < std::meta::enumerators_of(^^E).size() * 2) size *= 2;
return size;
}
@@ -147,8 +139,8 @@ constexpr auto identifier_slot(E value) -> size_t {
template ()>
consteval auto make_identifier_map()
- -> std::array, N> {
- auto map = std::array, N>();
+ -> std::array, N> {
+ auto map = std::array, N>();
for (std::meta::info e : std::meta::enumerators_of(^^E)) {
auto value = std::meta::extract(e);
for (auto i = identifier_slot(value);; i = (i + 1) & (N - 1)) {
@@ -157,7 +149,7 @@ consteval auto make_identifier_map()
if (map[i].first == value) break;
continue; // The slot is taken by another value; probe the next one.
}
- map[i] = {value, identifier(e)};
+ map[i] = {value, std::meta::identifier_of(e)};
break;
}
}
@@ -171,13 +163,13 @@ inline constexpr auto identifier_map = make_identifier_map();
// Returns the identifier of the first enumerator of E equal to value or an
// empty string view if there is no such enumerator.
-template constexpr auto identifier_of(E value) -> string_view {
+template constexpr auto identifier_of(E value) -> std::string_view {
constexpr size_t table_size = identifier_table_size();
if constexpr (table_size != 0) {
// Values outside of the table wrap around and are rejected by the check.
auto i = to_uint64(value) - to_uint64(min_enumerator());
return i < table_size ? identifier_table[static_cast(i)]
- : string_view();
+ : std::string_view();
} else {
constexpr size_t map_size = identifier_map_size();
// A free slot terminates the search and its empty identifier is the result.
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ce1c1f15..86966198 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -50,11 +50,6 @@ add_fmt_test(format-test mock-allocator.h)
if (MSVC)
target_compile_options(format-test PRIVATE /bigobj)
endif ()
-if (CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16)
- # format-test completes a type after it failed to be complete in a SFINAE
- # context, which is exactly what it is testing.
- target_compile_options(format-test PRIVATE -Wno-sfinae-incomplete)
-endif ()
if (NOT (MSVC AND BUILD_SHARED_LIBS))
add_fmt_test(format-impl-test HEADER_ONLY header-only-test.cc)
endif ()