Open edelmanjm opened 2 months ago
Possibly related to https://github.com/fmtlib/fmt/issues/4058? Unfortunately my templating isn't strong enough to figure this out definitively; I tried adding the following but it didn't work:
#include <fmt/ranges.h>
...
template <typename E>
struct fmt::is_range<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> {
static constexpr const bool value = false;
};
As for the local compiler error I'm seeing, it's as follows (trimmed for brevity):
ranges.h:674:47: error: ambiguous template instantiation for ‘struct fmt::v11::formatter<std::byte, char, void>’
674 | formatter<remove_cvref_t<value_type>, Char> value_formatter_;
| ^~~~~~~~~~~~~~~~
format.h:3975:8: note: candidates are: ‘template<class T, class Char> struct fmt::v11::formatter<T, Char, typename std::enable_if<fmt::v11::detail::has_format_as<T>::value, void>::type> [with T = std::byte; Char = char]’
3975 | struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
magic_enum_format.hpp:86:13: note: ‘template<class E> struct fmt::v11::formatter<E, typename std::enable_if<(is_enum_v<typename std::decay<_Tp>::type> && enum_format_enabled<E>()), char>::type> [with E = std::byte]’
86 | struct fmt::formatter<E, std::enable_if_t<std::is_enum_v<std::decay_t<E>> && magic_enum::customize::enum_format_enabled<E>(), char>> : fmt::formatter<std::string_view> {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So after a discussion with the author of fmt, I've worked out the following which appears to work:
template <typename E, std::enable_if_t<std::is_enum_v<std::decay_t<E>>, int> = 0>
auto format_as(E e) {
static_assert(std::is_same_v<char, fmt::string_view::value_type>, "formatter requires string_view::value_type type same as char.");
using D = std::decay_t<E>;
if constexpr (magic_enum::detail::supported<D>::value) {
if constexpr (magic_enum::detail::subtype_v<D> == magic_enum::detail::enum_subtype::flags) {
if (const auto name = magic_enum::enum_flags_name<D>(e); !name.empty()) {
return name;
}
} else {
if (const auto name = magic_enum::enum_name<D>(e); !name.empty()) {
return name;
}
}
}
return std::string_view(std::to_string(magic_enum::enum_integer<D>(e)));
}
I'm not confident this is necessarily the most optimal implementation, but I'm happy to upstream it if so desired.
The author of fmt has also made it clear that such weakly constrained specializations are considered poor practice. However, given the need to format many enums across namespaces, I'm not necessarily against continuing to include this in magic_enum.
Hi, please test https://github.com/Neargye/magic_enum/pull/382
Thanks, starting testing now.
Unfortunately this is not working for me; the formatter is not always detected.
cmake-build-debug-local/_deps/fmt-src/include/fmt/base.h:1641:63: error: ‘fmt::v11::detail::type_is_unformattable_for<Shared::Tests::CATCH2_INTERNAL_TEST_0()::MyEnum, char> _’ has incomplete type
1641 | type_is_unformattable_for<T, typename Context::char_type> _;
| ^
cmake-build-debug-local/_deps/fmt-src/include/fmt/base.h:1644:7: error: static assertion failed: Cannot format an argument. To make type T formattable provide a formatter<T> specialization: https://fmt.dev/latest/api.html#udt
1644 | formattable,
| ^~~~~~~~~~~
cmake-build-debug-local/_deps/fmt-src/include/fmt/base.h:1644:7: note: ‘formattable’ evaluates to false
I can see about providing a more minimal example if necessary.
@edelmanjm for me locally work, so I'll ask you to test it again (I update https://github.com/Neargye/magic_enum/pull/382)
I believe that's working! I'll do a bit more in-depth testing to confirm.
(Ported from a comment on #298)
Including both headers appears to result in a compiler error.
On Godbolt this appears to be a
magic_enum requires enum implementation and valid max and min.
(even though the default max and min should be sufficient, and it compiles fine as long asfmt/ranges.h
isn't included)On my local compiler with a more complex example, I get an ambiguous function error with the enum template showing as valid for a non-enum type (
std:vector<std::byte>
). I suspect this is closer to the actual error.Here's the failing example: with both
#include <fmt/ranges.h>
and#include <magic_enum/magic_enum_format.hpp>
, I get a compiler error.Removing
#include <fmt/ranges.h>
allows it to compileRemoving
#include <magic_enum/magic_enum_format.hpp>
also allows it to compile