magic_enum icon indicating copy to clipboard operation
magic_enum copied to clipboard

Including fmt/ranges.h and magic_enum/magic_enum_format.hpp fails to compile

Open edelmanjm opened this issue 1 year ago • 5 comments

(Ported from a comment on #298)

Including both headers appears to result in a compiler error.

edelmanjm avatar Sep 18 '24 14:09 edelmanjm

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> {
      |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

edelmanjm avatar Sep 19 '24 20:09 edelmanjm

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.

edelmanjm avatar Sep 24 '24 18:09 edelmanjm

Hi, please test https://github.com/Neargye/magic_enum/pull/382

Neargye avatar Oct 14 '24 15:10 Neargye

Thanks, starting testing now.

edelmanjm avatar Oct 17 '24 19:10 edelmanjm

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 avatar Oct 17 '24 19:10 edelmanjm

@edelmanjm for me locally work, so I'll ask you to test it again (I update https://github.com/Neargye/magic_enum/pull/382)

Neargye avatar Nov 13 '24 23:11 Neargye

I believe that's working! I'll do a bit more in-depth testing to confirm.

edelmanjm avatar Nov 27 '24 20:11 edelmanjm