-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Trying to improve errors in the unformattable case. #3478
Changes from 4 commits
b5bd632
805e84f
9b2ae5e
422181f
1323cd4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1539,23 +1539,43 @@ constexpr auto encode_types() -> unsigned long long { | |
(encode_types<Context, Args...>() << packed_arg_bits); | ||
} | ||
|
||
#if defined(__cpp_if_constexpr) | ||
// This type is intentionally undefined, only used for errors | ||
template <typename T, typename Char> struct type_is_unformattable_for; | ||
#endif | ||
|
||
template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)> | ||
FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> { | ||
using arg_type = remove_cvref_t<decltype(arg_mapper<Context>().map(val))>; | ||
|
||
constexpr bool formattable_char = | ||
!std::is_same<arg_type, unformattable_char>::value; | ||
#if defined(__cpp_if_constexpr) | ||
if constexpr (!formattable_char) { | ||
type_is_unformattable_for<T, typename Context::char_type> _; | ||
} | ||
#endif | ||
static_assert(formattable_char, "Mixing character types is disallowed."); | ||
|
||
// Formatting of arbitrary pointers is disallowed. If you want to format a | ||
// pointer cast it to `void*` or `const void*`. In particular, this forbids | ||
// formatting of `[const] volatile char*` printed as bool by iostreams. | ||
constexpr bool formattable_pointer = | ||
!std::is_same<arg_type, unformattable_pointer>::value; | ||
#if defined(__cpp_if_constexpr) | ||
if constexpr (!formattable_pointer) { | ||
type_is_unformattable_for<T, typename Context::char_type> _; | ||
} | ||
#endif | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. |
||
static_assert(formattable_pointer, | ||
"Formatting of non-void pointers is disallowed."); | ||
|
||
constexpr bool formattable = !std::is_same<arg_type, unformattable>::value; | ||
#if defined(__cpp_if_constexpr) | ||
if constexpr (!formattable) { | ||
type_is_unformattable_for<T, typename Context::char_type> _; | ||
} | ||
#endif | ||
static_assert( | ||
formattable, | ||
"Cannot format an argument. To make type T formattable provide a " | ||
|
@@ -2520,7 +2540,17 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx) | |
mapped_type_constant<T, context>::value != type::custom_type, | ||
decltype(arg_mapper<context>().map(std::declval<const T&>())), | ||
typename strip_named_arg<T>::type>; | ||
#if defined(__cpp_if_constexpr) | ||
if constexpr (std::is_default_constructible_v< | ||
formatter<mapped_type, char_type>>) { | ||
return formatter<mapped_type, char_type>().parse(ctx); | ||
} else { | ||
type_is_unformattable_for<T, char_type> _; | ||
return ctx.begin(); | ||
} | ||
#else | ||
Comment on lines
+2533
to
+2541
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to duplicate the logic here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I could do something like this instead: if constexpr (/* not default constructible */) {
type_is_unformattable_for<T, char_type> _;
}
return formatter<mapped_type, char_type>().parse(Ctx); But doing it this way gives you both the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure why we care about checking default constructibility at all. It should be uncommon and not worth any extra diagnostics. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Because that's what actually fails here. Today, if
That's the only way of checking if something is formattable, as far as I'm aware. |
||
return formatter<mapped_type, char_type>().parse(ctx); | ||
#endif | ||
} | ||
|
||
// Checks char specs and returns true iff the presentation type is char-like. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the message below better explains the error, so let's remove this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You still get both errors - the
static_assert
message is still printed. This way you also get which character type (and pointer type, for the next one) you were trying to print, which I think is still helpful info.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, is this information actually useful? You cannot make the types in question formattable and I think having two errors may be confusing.