diff --git a/include/fmt/core.h b/include/fmt/core.h index 79b9298a..9a2668df 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1301,9 +1301,6 @@ template class value { } }; -template -FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg; - // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. enum { long_short = sizeof(long) == sizeof(int) }; @@ -1497,110 +1494,6 @@ class appender : public std::back_insert_iterator> { auto operator++(int) noexcept -> appender { return *this; } }; -// A formatting argument. It is a trivially copyable/constructible type to -// allow storage in basic_memory_buffer. -template class basic_format_arg { - private: - detail::value value_; - detail::type type_; - - template - friend FMT_CONSTEXPR auto detail::make_arg(T&& value) - -> basic_format_arg; - - template - friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, - const basic_format_arg& arg) - -> decltype(vis(0)); - - friend class basic_format_args; - friend class dynamic_format_arg_store; - - using char_type = typename Context::char_type; - - template - friend struct detail::arg_data; - - basic_format_arg(const detail::named_arg_info* args, size_t size) - : value_(args, size) {} - - public: - class handle { - public: - explicit handle(detail::custom_value custom) : custom_(custom) {} - - void format(typename Context::parse_context_type& parse_ctx, - Context& ctx) const { - custom_.format(custom_.value, parse_ctx, ctx); - } - - private: - detail::custom_value custom_; - }; - - constexpr basic_format_arg() : type_(detail::type::none_type) {} - - constexpr explicit operator bool() const noexcept { - return type_ != detail::type::none_type; - } - - auto type() const -> detail::type { return type_; } - - auto is_integral() const -> bool { return detail::is_integral_type(type_); } - auto is_arithmetic() const -> bool { - return detail::is_arithmetic_type(type_); - } -}; - -/** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ -FMT_MODULE_EXPORT -template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( - Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { - switch (arg.type_) { - case detail::type::none_type: - break; - case detail::type::int_type: - return vis(arg.value_.int_value); - case detail::type::uint_type: - return vis(arg.value_.uint_value); - case detail::type::long_long_type: - return vis(arg.value_.long_long_value); - case detail::type::ulong_long_type: - return vis(arg.value_.ulong_long_value); - case detail::type::int128_type: - return vis(detail::convert_for_visit(arg.value_.int128_value)); - case detail::type::uint128_type: - return vis(detail::convert_for_visit(arg.value_.uint128_value)); - case detail::type::bool_type: - return vis(arg.value_.bool_value); - case detail::type::char_type: - return vis(arg.value_.char_value); - case detail::type::float_type: - return vis(arg.value_.float_value); - case detail::type::double_type: - return vis(arg.value_.double_value); - case detail::type::long_double_type: - return vis(arg.value_.long_double_value); - case detail::type::cstring_type: - return vis(arg.value_.string.data); - case detail::type::string_type: - using sv = basic_string_view; - return vis(sv(arg.value_.string.data, arg.value_.string.size)); - case detail::type::pointer_type: - return vis(arg.value_.pointer); - case detail::type::custom_type: - return vis(typename basic_format_arg::handle(arg.value_.custom)); - } - return vis(monostate()); -} - namespace detail { template @@ -1695,30 +1588,128 @@ FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value { } template -FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg { +FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg { auto arg = basic_format_arg(); arg.type_ = mapped_type_constant::value; - arg.value_ = make_value(value); + arg.value_ = make_value(val); return arg; } -// The DEPRECATED 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 -FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value { +template +FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value { return make_value(val); } - -template -FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg { - return make_arg(value); +template +FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg { + return make_arg(val); } } // namespace detail FMT_BEGIN_EXPORT +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in basic_memory_buffer. +template class basic_format_arg { + private: + detail::value value_; + detail::type type_; + + template + friend FMT_CONSTEXPR auto detail::make_arg(T& value) + -> basic_format_arg; + + template + friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis, + const basic_format_arg& arg) + -> decltype(vis(0)); + + friend class basic_format_args; + friend class dynamic_format_arg_store; + + using char_type = typename Context::char_type; + + template + friend struct detail::arg_data; + + basic_format_arg(const detail::named_arg_info* args, size_t size) + : value_(args, size) {} + + public: + class handle { + public: + explicit handle(detail::custom_value custom) : custom_(custom) {} + + void format(typename Context::parse_context_type& parse_ctx, + Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } + + private: + detail::custom_value custom_; + }; + + constexpr basic_format_arg() : type_(detail::type::none_type) {} + + constexpr explicit operator bool() const noexcept { + return type_ != detail::type::none_type; + } + + auto type() const -> detail::type { return type_; } + + auto is_integral() const -> bool { return detail::is_integral_type(type_); } + auto is_arithmetic() const -> bool { + return detail::is_arithmetic_type(type_); + } +}; + +/** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + ``vis(value)`` will be called with the value of type ``double``. + \endrst + */ +FMT_MODULE_EXPORT +template +FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( + Visitor&& vis, const basic_format_arg& arg) -> decltype(vis(0)) { + switch (arg.type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(arg.value_.int_value); + case detail::type::uint_type: + return vis(arg.value_.uint_value); + case detail::type::long_long_type: + return vis(arg.value_.long_long_value); + case detail::type::ulong_long_type: + return vis(arg.value_.ulong_long_value); + case detail::type::int128_type: + return vis(detail::convert_for_visit(arg.value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(arg.value_.uint128_value)); + case detail::type::bool_type: + return vis(arg.value_.bool_value); + case detail::type::char_type: + return vis(arg.value_.char_value); + case detail::type::float_type: + return vis(arg.value_.float_value); + case detail::type::double_type: + return vis(arg.value_.double_value); + case detail::type::long_double_type: + return vis(arg.value_.long_double_value); + case detail::type::cstring_type: + return vis(arg.value_.string.data); + case detail::type::string_type: + using sv = basic_string_view; + return vis(sv(arg.value_.string.data, arg.value_.string.size)); + case detail::type::pointer_type: + return vis(arg.value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(arg.value_.custom)); + } + return vis(monostate()); +} + // Formatting context. template class basic_format_context { private: @@ -1822,9 +1813,7 @@ class format_arg_store #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 basic_format_args(*this), #endif - data_{detail::make_arg< - is_packed, Context, - detail::mapped_type_constant::value>(args)...} { + data_{detail::make_arg(args)...} { detail::init_named_args(data_.named_args(), 0, 0, args...); } }; diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 554715e9..1fb5e0f7 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -132,22 +132,23 @@ template class arg_converter { if (const_check(sizeof(target_type) <= sizeof(int))) { // Extra casts are used to silence warnings. if (is_signed) { - arg_ = detail::make_arg( - static_cast(static_cast(value))); + auto n = static_cast(static_cast(value)); + arg_ = detail::make_arg(n); } else { using unsigned_type = typename make_unsigned_or_bool::type; - arg_ = detail::make_arg( - static_cast(static_cast(value))); + auto n = static_cast(static_cast(value)); + arg_ = detail::make_arg(n); } } else { if (is_signed) { // glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB. - arg_ = detail::make_arg(static_cast(value)); + auto n = static_cast(value); + arg_ = detail::make_arg(n); } else { - arg_ = detail::make_arg( - static_cast::type>(value)); + auto n = static_cast::type>(value); + arg_ = detail::make_arg(n); } } } @@ -175,8 +176,8 @@ template class char_converter { template ::value)> void operator()(T value) { - arg_ = detail::make_arg( - static_cast(value)); + auto c = static_cast(value); + arg_ = detail::make_arg(c); } template ::value)> @@ -476,9 +477,9 @@ void vprintf(buffer& buf, basic_string_view format, auto str = visit_format_arg(get_cstring(), arg); auto str_end = str + specs.precision; auto nul = std::find(str, str_end, Char()); - arg = make_arg>( - basic_string_view( - str, to_unsigned(nul != str_end ? nul - str : specs.precision))); + auto sv = basic_string_view( + str, to_unsigned(nul != str_end ? nul - str : specs.precision)); + arg = make_arg>(sv); } if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; if (specs.fill[0] == '0') { diff --git a/test/core-test.cc b/test/core-test.cc index ff9d4411..dbb19017 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -361,10 +361,11 @@ VISIT_TYPE(unsigned long, unsigned long long); testing::StrictMock> visitor; \ EXPECT_CALL(visitor, visit(expected)); \ using iterator = std::back_insert_iterator>; \ + auto var = value; \ fmt::visit_format_arg( \ visitor, \ fmt::detail::make_arg>( \ - value)); \ + var)); \ } #define CHECK_ARG_SIMPLE(value) \ diff --git a/test/format-test.cc b/test/format-test.cc index 6e8578a0..fb63f149 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1773,20 +1773,6 @@ TEST(format_test, variadic) { EXPECT_EQ("abc1", fmt::format("{}c{}", "ab", 1)); } -TEST(format_test, dynamic) { - using ctx = fmt::format_context; - auto args = std::vector>(); - args.emplace_back(fmt::detail::make_arg(42)); - args.emplace_back(fmt::detail::make_arg("abc1")); - args.emplace_back(fmt::detail::make_arg(1.5f)); - - std::string result = fmt::vformat( - "{} and {} and {}", - fmt::format_args(args.data(), static_cast(args.size()))); - - EXPECT_EQ("42 and abc1 and 1.5", result); -} - TEST(format_test, bytes) { auto s = fmt::format("{:10}", fmt::bytes("ёжик")); EXPECT_EQ("ёжик ", s); @@ -2098,8 +2084,8 @@ TEST(format_test, format_to_n_output_iterator) { TEST(format_test, vformat_to) { using context = fmt::format_context; - fmt::basic_format_arg arg = fmt::detail::make_arg(42); - auto args = fmt::basic_format_args(&arg, 1); + int n = 42; + auto args = fmt::make_format_args(n); auto s = std::string(); fmt::vformat_to(std::back_inserter(s), "{}", args); EXPECT_EQ("42", s); diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 164c7778..b5cf3351 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -146,14 +146,9 @@ TEST(xchar_test, format_to) { } TEST(xchar_test, vformat_to) { - using wcontext = fmt::wformat_context; - fmt::basic_format_arg warg = fmt::detail::make_arg(42); - auto wargs = fmt::basic_format_args(&warg, 1); + auto args = fmt::make_wformat_args(42); auto w = std::wstring(); - fmt::vformat_to(std::back_inserter(w), L"{}", wargs); - EXPECT_EQ(L"42", w); - w.clear(); - fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs); + fmt::vformat_to(std::back_inserter(w), L"{}", args); EXPECT_EQ(L"42", w); }