Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
edelmanjm opened this issue Sep 18, 2024 · 7 comments
Assignees
Milestone

Comments

@edelmanjm
Copy link

edelmanjm commented Sep 18, 2024

(Ported from a comment on #298)

Including both headers appears to result in a compiler error.

@Neargye Neargye added this to the v0.9.7 milestone Sep 19, 2024
@Neargye Neargye self-assigned this Sep 19, 2024
@edelmanjm
Copy link
Author

Possibly related to fmtlib/fmt#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
Copy link
Author

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.

@Neargye
Copy link
Owner

Neargye commented Oct 14, 2024

Hi, please test #382

@edelmanjm
Copy link
Author

Thanks, starting testing now.

@edelmanjm
Copy link
Author

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.

@Neargye Neargye modified the milestones: v0.9.7, v0.9.8 Nov 13, 2024
@Neargye
Copy link
Owner

Neargye commented Nov 13, 2024

@edelmanjm for me locally work, so I'll ask you to test it again (I update #382)

@edelmanjm
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants