bers
bers

Reputation: 5813

What do I have to do to make an enum class std::formattable?

I am about to switch from C++20 to C++23, which has std::formattable.

In C++20, I had used the formattable from https://stackoverflow.com/a/72430675, but it turns out std::formattable works differently. In effect, classes that are formattable are not necessarily std::formattable, and I wonder why. Here's one example:

#include <format>
#include <string>
#include <type_traits>

enum class RandomEnum { A, B };

// Formatter that formats everything as "ABC"
template <>
struct std::formatter<RandomEnum> : std::formatter<std::string> {
    auto format(const RandomEnum t, std::format_context& ctx) const {
        return std::formatter<std::string>::format("ABC", ctx);
    }
};

// Pre-C++23 version of std::formattable
template <typename T>
concept formattable = requires(T& v, std::format_context ctx) {
    std::formatter<std::remove_cvref_t<T>>().format(v, ctx);
};

int main() {
    // Returns 2: class is formattable but not std::formattable:
    return (formattable<RandomEnum> << 1) + std::formattable<RandomEnum, char>;
}

How can I make my enum classes std::formattable?

This code examples gives an error message in MSVC and clang, but that doesn't help me much, either:

#include <format>
#include <string>
#include <type_traits>

enum class RandomEnum { A, B };

template <>
struct std::formatter<RandomEnum> : std::formatter<std::string> {
    auto format(const RandomEnum t, std::format_context& ctx) const {
        return std::formatter<std::string>::format("ABC", ctx);
    }
};

int fun(std::formattable<char> auto x) { return 0; }

int main() { return fun(RandomEnum::A); }

MSVC:

<source>(16): error C2672: 'fun': no matching overloaded function found
<source>(14): note: could be 'int fun(_T0)'
<source>(16): note: the associated constraints are not satisfied
<source>(14): note: the concept 'std::formattable<RandomEnum,char>' evaluated to false
Z:/compilers/msvc/14.41.33923-14.41.33923.0/include\format(2255): note: the concept 'std::_Formattable_with<RandomEnum,std::basic_format_context<std::_Phony_fmt_iter_for<char>,char>,std::formatter<RandomEnum,char>>' evaluated to false
Z:/compilers/msvc/14.41.33923-14.41.33923.0/include\format(658): note: 'std::back_insert_iterator<std::_Fmt_buffer<char>> std::formatter<RandomEnum,char>::format(const RandomEnum,std::format_context &) const': cannot convert argument 2 from 'std::basic_format_context<std::_Phony_fmt_iter_for<char>,char>' to 'std::format_context &'
Z:/compilers/msvc/14.41.33923-14.41.33923.0/include\format(658): note: while trying to match the argument list '(RandomEnum, std::basic_format_context<std::_Phony_fmt_iter_for<char>,char>)'

clang:

<source>:16:21: error: no matching function for call to 'fun'
   16 | int main() { return fun(RandomEnum::A); }
      |                     ^~~
<source>:14:5: note: candidate template ignored: constraints not satisfied [with x:auto = RandomEnum]
   14 | int fun(std::formattable<char> auto x) { return 0; }
      |     ^
<source>:14:9: note: because 'std::formattable<RandomEnum, char>' evaluated to false
   14 | int fun(std::formattable<char> auto x) { return 0; }
      |         ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2548:9: note: because '__format::__formattable_impl<remove_reference_t<RandomEnum>, char>' evaluated to false
 2548 |       = __format::__formattable_impl<remove_reference_t<_Tp>, _CharT>;
      |         ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2539:43: note: because '__formattable_with<RandomEnum, std::basic_format_context<std::back_insert_iterator<std::basic_string<char> >, char> >' evaluated to false
 2539 |       = __parsable_with<_Tp, _Context> && __formattable_with<_Tp, _Context>;
      |                                           ^
/opt/compiler-explorer/gcc-14.2.0/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../include/c++/14.2.0/format:2529:26: note: because '__cf.format(__t, __fc)' would be invalid: non-const lvalue reference to type 'basic_format_context<<U+007F>_Sink_iter<char><U+007F>, [...]>' cannot bind to a value of unrelated type 'basic_format_context<<U+007F>std::back_insert_iterator<std::basic_string<char>><U+007F>, [...]>'
 2529 |       { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
      |                          ^
1 error generated.

Upvotes: 2

Views: 112

Answers (0)

Related Questions