diff --git a/include/fmt/core.h b/include/fmt/core.h index 80d93dff..891555d4 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -973,6 +973,8 @@ enum { long_short = sizeof(long) == sizeof(int) }; using long_type = conditional_t; using ulong_type = conditional_t; +struct unformattable {}; + // Maps formatting arguments to core types. template struct arg_mapper { using char_type = typename Context::char_type; @@ -1081,15 +1083,7 @@ template struct arg_mapper { return map(val.value); } - int map(...) { - constexpr bool formattable = sizeof(Context) == 0; - static_assert( - formattable, - "Cannot format argument. To make type T formattable provide a " - "formatter specialization: " - "https://fmt.dev/latest/api.html#formatting-user-defined-types"); - return 0; - } + unformattable map(...) { return {}; } }; // A type constant after applying arg_mapper. @@ -1264,13 +1258,27 @@ FMT_CONSTEXPR basic_format_arg make_arg(const T& value) { return arg; } +template struct formattable : std::false_type {}; + +template int check(unformattable) { + static_assert( + formattable(), + "Cannot format argument. To make type T formattable provide a " + "formatter specialization: " + "https://fmt.dev/latest/api.html#formatting-user-defined-types"); + return 0; +} +template inline const U& check(const U& val) { + return val; +} + // The type template parameter is there to avoid an ODR violation when using // a fallback formatter in one translation unit and an implicit conversion in // another (not recommended). template inline value make_arg(const T& val) { - return arg_mapper().map(val); + return check(arg_mapper().map(val)); } template