forked from fmtlib/fmt
Improve error reporting
This commit is contained in:
@@ -1116,6 +1116,9 @@ constexpr bool is_arithmetic_type(type t) {
|
|||||||
return t > type::none_type && t <= type::last_numeric_type;
|
return t > type::none_type && t <= type::last_numeric_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct unformattable {};
|
||||||
|
struct unformattable_pointer : unformattable {};
|
||||||
|
|
||||||
template <typename Char> struct string_value {
|
template <typename Char> struct string_value {
|
||||||
const Char* data;
|
const Char* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
@@ -1192,6 +1195,8 @@ template <typename Context> class value {
|
|||||||
typename Context::template formatter_type<value_type>,
|
typename Context::template formatter_type<value_type>,
|
||||||
fallback_formatter<value_type, char_type>>>;
|
fallback_formatter<value_type, char_type>>>;
|
||||||
}
|
}
|
||||||
|
value(unformattable);
|
||||||
|
value(unformattable_pointer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Formats an argument of a custom type, such as a user-defined class.
|
// Formats an argument of a custom type, such as a user-defined class.
|
||||||
@@ -1216,8 +1221,6 @@ enum { long_short = sizeof(long) == sizeof(int) };
|
|||||||
using long_type = conditional_t<long_short, int, long long>;
|
using long_type = conditional_t<long_short, int, long long>;
|
||||||
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
||||||
|
|
||||||
struct unformattable {};
|
|
||||||
|
|
||||||
// Maps formatting arguments to core types.
|
// Maps formatting arguments to core types.
|
||||||
template <typename Context> struct arg_mapper {
|
template <typename Context> struct arg_mapper {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
@@ -1320,16 +1323,17 @@ template <typename Context> struct arg_mapper {
|
|||||||
// We use SFINAE instead of a const T* parameter to avoid conflicting with
|
// We use SFINAE instead of a const T* parameter to avoid conflicting with
|
||||||
// the C array overload.
|
// the C array overload.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
FMT_CONSTEXPR auto map(T) -> enable_if_t<std::is_pointer<T>::value, int> {
|
FMT_CONSTEXPR auto map(T)
|
||||||
|
-> enable_if_t<std::is_pointer<T>::value, unformattable_pointer> {
|
||||||
// Formatting of arbitrary pointers is disallowed. If you want to output
|
// Formatting of arbitrary pointers is disallowed. If you want to output
|
||||||
// a pointer cast it to "void *" or "const void *". In particular, this
|
// a pointer cast it to "void *" or "const void *". In particular, this
|
||||||
// forbids formatting of "[const] volatile char *" which is printed as bool
|
// forbids formatting of "[const] volatile char *" which is printed as bool
|
||||||
// by iostreams.
|
// by iostreams.
|
||||||
static_assert(!sizeof(T), "formatting of non-void pointers is disallowed");
|
return {};
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, std::size_t N>
|
template <typename T, std::size_t N,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<T, wchar_t>::value)>
|
||||||
FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
|
FMT_CONSTEXPR FMT_INLINE auto map(const T (&values)[N]) -> const T (&)[N] {
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
@@ -1589,8 +1593,14 @@ template <bool IS_PACKED, typename Context, type, typename T,
|
|||||||
FMT_ENABLE_IF(IS_PACKED)>
|
FMT_ENABLE_IF(IS_PACKED)>
|
||||||
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
||||||
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
|
const auto& arg = arg_mapper<Context>().map(std::forward<T>(val));
|
||||||
|
constexpr bool void_ptr =
|
||||||
|
!std::is_same<decltype(arg), const unformattable_pointer&>::value;
|
||||||
|
static_assert(void_ptr,
|
||||||
|
"Formatting of non-void pointers is disallowed.");
|
||||||
|
constexpr bool formattable =
|
||||||
|
!std::is_same<decltype(arg), const unformattable&>::value;
|
||||||
static_assert(
|
static_assert(
|
||||||
!std::is_same<decltype(arg), const unformattable&>::value,
|
formattable,
|
||||||
"Cannot format an argument. To make type T formattable provide a "
|
"Cannot format an argument. To make type T formattable provide a "
|
||||||
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
|
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
|
||||||
return {arg};
|
return {arg};
|
||||||
@@ -1668,9 +1678,9 @@ using format_context = buffer_context<char>;
|
|||||||
|
|
||||||
template <typename T, typename Char = char>
|
template <typename T, typename Char = char>
|
||||||
using is_formattable = bool_constant<
|
using is_formattable = bool_constant<
|
||||||
!std::is_same<decltype(detail::arg_mapper<buffer_context<Char>>().map(
|
!std::is_base_of<detail::unformattable,
|
||||||
std::declval<T>())),
|
decltype(detail::arg_mapper<buffer_context<Char>>().map(
|
||||||
detail::unformattable>::value &&
|
std::declval<T>()))>::value &&
|
||||||
!detail::has_fallback_formatter<T, Char>::value>;
|
!detail::has_fallback_formatter<T, Char>::value>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -408,15 +408,7 @@ TYPED_TEST(numeric_arg_test, make_and_visit) {
|
|||||||
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
|
CHECK_ARG_SIMPLE(std::numeric_limits<TypeParam>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace fmt {
|
TEST(arg_test, char_arg) { CHECK_ARG(char, 'a', 'a'); }
|
||||||
template <> struct is_char<wchar_t> : std::true_type {};
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
TEST(arg_test, char_arg) {
|
|
||||||
CHECK_ARG(char, 'a', 'a');
|
|
||||||
CHECK_ARG(wchar_t, L'a', 'a');
|
|
||||||
CHECK_ARG(wchar_t, L'a', L'a');
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(arg_test, string_arg) {
|
TEST(arg_test, string_arg) {
|
||||||
char str_data[] = "test";
|
char str_data[] = "test";
|
||||||
@@ -712,6 +704,8 @@ TEST(core_test, has_formatter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(core_test, is_formattable) {
|
TEST(core_test, is_formattable) {
|
||||||
|
static_assert(!fmt::is_formattable<const wchar_t*>::value, "");
|
||||||
|
static_assert(!fmt::is_formattable<const wchar_t[3]>::value, "");
|
||||||
static_assert(fmt::is_formattable<enabled_formatter>::value, "");
|
static_assert(fmt::is_formattable<enabled_formatter>::value, "");
|
||||||
static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
|
static_assert(!fmt::is_formattable<disabled_formatter>::value, "");
|
||||||
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
|
static_assert(fmt::is_formattable<disabled_formatter_convertible>::value, "");
|
||||||
|
Reference in New Issue
Block a user