diff --git a/include/fmt/args.h b/include/fmt/args.h index a24fe3f1..d84d840f 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -124,7 +124,7 @@ class dynamic_format_arg_store void emplace_arg(const detail::named_arg& arg) { if (named_info_.empty()) { constexpr const detail::named_arg_info* zero_ptr{nullptr}; - data_.insert(data_.begin(), {zero_ptr, 0}); + data_.insert(data_.begin(), basic_format_arg(zero_ptr, 0)); } data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); auto pop_one = [](std::vector>* data) { diff --git a/include/fmt/base.h b/include/fmt/base.h index dce02fa1..28df1e4b 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2174,7 +2174,8 @@ template struct custom_value { void (*format)(void* arg, parse_context& parse_ctx, Context& ctx); }; -enum class custom_tag {}; +// This type is intentionally undefined, only used for errors. +template struct type_is_unformattable_for; // A formatting argument value. template class value { @@ -2223,10 +2224,33 @@ template class value { } FMT_ALWAYS_INLINE value(const void* val) : pointer(val) {} - template - FMT_CONSTEXPR20 FMT_ALWAYS_INLINE value(T& val, custom_tag = {}) { - using value_type = typename std::remove_cv::type; + template FMT_CONSTEXPR20 FMT_ALWAYS_INLINE value(T& val) { + // Use enum instead of constexpr because the latter may generate code. + enum { formattable_char = !std::is_same::value }; + 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. + enum { + formattable_pointer = !std::is_same::value + }; + static_assert(formattable_pointer, + "formatting of non-void pointers is disallowed"); + + enum { formattable = !std::is_same::value }; +#if defined(__cpp_if_constexpr) + if constexpr (!formattable) type_is_unformattable_for _; + // if constexpr (!Context::builtin_types && !std::is_same::value) + // return {unwrap_named_arg(val), custom_tag()}; +#endif + static_assert( + formattable, + "cannot format an argument; to make type T formattable provide a " + "formatter specialization: https://fmt.dev/latest/api.html#udt"); + // T may overload operator& e.g. std::vector::reference in libc++. + using value_type = typename std::remove_cv::type; #if defined(__cpp_if_constexpr) if constexpr (std::is_same::value) custom.value = const_cast(&val); @@ -2241,9 +2265,6 @@ template class value { } FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} - value(unformattable); - value(unformattable_char); - value(unformattable_pointer); private: // Formats an argument of a custom type, such as a user-defined class. @@ -2317,40 +2338,9 @@ constexpr unsigned long long make_descriptor() { : is_unpacked_bit | NUM_ARGS; } -// This type is intentionally undefined, only used for errors. -template struct type_is_unformattable_for; - template FMT_CONSTEXPR auto make_arg(T& val) -> value { - using char_type = typename Context::char_type; - using arg_type = remove_cvref_t::map(val))>; - - // Use enum instead of constexpr because the latter may generate code. - enum { - formattable_char = !std::is_same::value - }; - 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. - enum { - formattable_pointer = !std::is_same::value - }; - static_assert(formattable_pointer, - "formatting of non-void pointers is disallowed"); - - enum { formattable = !std::is_same::value }; -#if defined(__cpp_if_constexpr) - if constexpr (!formattable) type_is_unformattable_for _; - if constexpr (!Context::builtin_types && !std::is_same::value) - return {unwrap_named_arg(val), custom_tag()}; -#endif - static_assert( - formattable, - "cannot format an argument; to make type T formattable provide a " - "formatter specialization: https://fmt.dev/latest/api.html#udt"); - return {arg_mapper::map(val)}; + return {arg_mapper::map(val)}; } template @@ -2378,9 +2368,9 @@ template args[ARGS_ARR_SIZE]; + arg_t args[ARGS_ARRAY_SIZE]; named_arg_info named_args[NUM_NAMED_ARGS]; template @@ -2394,7 +2384,7 @@ struct format_arg_store { format_arg_store(format_arg_store&& rhs) { args[0] = {named_args, NUM_NAMED_ARGS}; - for (size_t i = 1; i < ARGS_ARR_SIZE; ++i) args[i] = rhs.args[i]; + for (size_t i = 1; i < ARGS_ARRAY_SIZE; ++i) args[i] = rhs.args[i]; for (size_t i = 0; i < NUM_NAMED_ARGS; ++i) named_args[i] = rhs.named_args[i]; } @@ -2548,6 +2538,9 @@ template class basic_format_arg { constexpr basic_format_arg() : type_(detail::type::none_type) {} basic_format_arg(const detail::named_arg_info* args, size_t size) : value_(args, size) {} + template + basic_format_arg(T&& val) + : value_(val), type_(detail::stored_type_constant::value) {} constexpr explicit operator bool() const noexcept { return type_ != detail::type::none_type; @@ -2848,8 +2841,7 @@ template constexpr FMT_ALWAYS_INLINE auto make_format_args(T&... args) -> detail::format_arg_store { - return {{detail::make_arg( - args)...}}; + return {{{detail::arg_mapper::map(args)}...}}; } #ifndef FMT_DOC