From 068bf9bad8a6f96a9a47477939b4699f14ebeac5 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 1 Jan 2024 17:31:36 -0800 Subject: [PATCH] Make visitation compatible with std::format --- include/fmt/core.h | 113 ++++++++++++++++++++++++------------------- include/fmt/format.h | 30 +++--------- include/fmt/printf.h | 18 +++---- test/core-test.cc | 28 ++++++----- 4 files changed, 95 insertions(+), 94 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 35781df3..cd2ee96d 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -88,6 +88,20 @@ #define FMT_HAS_CPP17_ATTRIBUTE(attribute) \ (FMT_CPLUSPLUS >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute)) +#ifndef FMT_DEPRECATED +# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 +# define FMT_DEPRECATED [[deprecated]] +# else +# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) +# define FMT_DEPRECATED __attribute__((deprecated)) +# elif FMT_MSC_VERSION +# define FMT_DEPRECATED __declspec(deprecated) +# else +# define FMT_DEPRECATED /* deprecated */ +# endif +# endif +#endif + // Check if relaxed C++14 constexpr is supported. // GCC doesn't allow throw in constexpr until version 6 (bug 67371). #ifndef FMT_USE_CONSTEXPR @@ -1624,11 +1638,6 @@ template class basic_format_arg { 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; @@ -1667,6 +1676,53 @@ template class basic_format_arg { 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 + */ + template + FMT_CONSTEXPR auto visit(Visitor&& vis) -> decltype(vis(0)) { + switch (type_) { + case detail::type::none_type: + break; + case detail::type::int_type: + return vis(value_.int_value); + case detail::type::uint_type: + return vis(value_.uint_value); + case detail::type::long_long_type: + return vis(value_.long_long_value); + case detail::type::ulong_long_type: + return vis(value_.ulong_long_value); + case detail::type::int128_type: + return vis(detail::convert_for_visit(value_.int128_value)); + case detail::type::uint128_type: + return vis(detail::convert_for_visit(value_.uint128_value)); + case detail::type::bool_type: + return vis(value_.bool_value); + case detail::type::char_type: + return vis(value_.char_value); + case detail::type::float_type: + return vis(value_.float_value); + case detail::type::double_type: + return vis(value_.double_value); + case detail::type::long_double_type: + return vis(value_.long_double_value); + case detail::type::cstring_type: + return vis(value_.string.data); + case detail::type::string_type: + using sv = basic_string_view; + return vis(sv(value_.string.data, value_.string.size)); + case detail::type::pointer_type: + return vis(value_.pointer); + case detail::type::custom_type: + return vis(typename basic_format_arg::handle(value_.custom)); + } + return vis(monostate()); + } + FMT_INLINE auto format_custom(const char_type* parse_begin, typename Context::parse_context_type& parse_ctx, Context& ctx) -> bool { @@ -1677,53 +1733,10 @@ template class basic_format_arg { } }; -/** - \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 - */ -// DEPRECATED! template -FMT_CONSTEXPR FMT_INLINE auto visit_format_arg( +FMT_DEPRECATED 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()); + return arg.visit(std::forward(vis)); } // Formatting context. diff --git a/include/fmt/format.h b/include/fmt/format.h index 0b7483af..702ef3fb 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -65,20 +65,6 @@ # define FMT_FALLTHROUGH #endif -#ifndef FMT_DEPRECATED -# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VERSION >= 1900 -# define FMT_DEPRECATED [[deprecated]] -# else -# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__) -# define FMT_DEPRECATED __attribute__((deprecated)) -# elif FMT_MSC_VERSION -# define FMT_DEPRECATED __declspec(deprecated) -# else -# define FMT_DEPRECATED /* deprecated */ -# endif -# endif -#endif - #ifndef FMT_NO_UNIQUE_ADDRESS # if FMT_CPLUSPLUS >= 202002L # if FMT_HAS_CPP_ATTRIBUTE(no_unique_address) @@ -1053,7 +1039,7 @@ class loc_value { loc_value(T) {} template auto visit(Visitor&& vis) -> decltype(vis(0)) { - return visit_format_arg(vis, value_); + return value_.visit(vis); } }; @@ -3831,7 +3817,7 @@ struct precision_checker { template FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int { - unsigned long long value = visit_format_arg(Handler(), arg); + unsigned long long value = arg.visit(Handler()); if (value > to_unsigned(max_value())) throw_format_error("number is too big"); return static_cast(value); @@ -4278,7 +4264,7 @@ void vformat_to(buffer& buf, basic_string_view fmt, if (fmt.size() == 2 && equal2(fmt.data(), "{}")) { auto arg = args.get(0); if (!arg) throw_format_error("argument not found"); - visit_format_arg(default_arg_formatter{out, args, loc}, arg); + arg.visit(default_arg_formatter{out, args, loc}); return; } @@ -4310,10 +4296,8 @@ void vformat_to(buffer& buf, basic_string_view fmt, FMT_INLINE void on_replacement_field(int id, const Char*) { auto arg = get_arg(context, id); - context.advance_to(visit_format_arg( - default_arg_formatter{context.out(), context.args(), - context.locale()}, - arg)); + context.advance_to(arg.visit(default_arg_formatter{ + context.out(), context.args(), context.locale()})); } auto on_format_specs(int id, const Char* begin, const Char* end) @@ -4330,8 +4314,8 @@ void vformat_to(buffer& buf, basic_string_view fmt, specs.precision, specs.precision_ref, context); if (begin == end || *begin != '}') throw_format_error("missing '}' in format string"); - auto f = arg_formatter{context.out(), specs, context.locale()}; - context.advance_to(visit_format_arg(f, arg)); + context.advance_to(arg.visit( + arg_formatter{context.out(), specs, context.locale()})); return begin; } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index b70df5e7..35445abc 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -163,7 +163,7 @@ template class arg_converter { // unsigned). template void convert_arg(basic_format_arg& arg, Char type) { - visit_format_arg(arg_converter(arg, type), arg); + arg.visit(arg_converter(arg, type)); } // Converts an integer argument to char for printf. @@ -366,8 +366,8 @@ auto parse_header(const Char*& it, const Char* end, format_specs& specs, if (specs.width == -1) throw_format_error("number is too big"); } else if (*it == '*') { ++it; - specs.width = static_cast(visit_format_arg( - detail::printf_width_handler(specs), get_arg(-1))); + specs.width = static_cast( + get_arg(-1).visit(detail::printf_width_handler(specs))); } } return arg_index; @@ -462,8 +462,8 @@ void vprintf(buffer& buf, basic_string_view format, specs.precision = parse_nonnegative_int(it, end, 0); } else if (c == '*') { ++it; - specs.precision = static_cast( - visit_format_arg(printf_precision_handler(), get_arg(-1))); + specs.precision = + static_cast(get_arg(-1).visit(printf_precision_handler())); } else { specs.precision = 0; } @@ -477,14 +477,14 @@ void vprintf(buffer& buf, basic_string_view format, specs.fill[0] = ' '; } if (specs.precision >= 0 && arg.type() == type::cstring_type) { - auto str = visit_format_arg(get_cstring(), arg); + auto str = arg.visit(get_cstring()); auto str_end = str + specs.precision; auto nul = std::find(str, str_end, Char()); 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.alt && arg.visit(is_zero_int())) specs.alt = false; if (specs.fill[0] == '0') { if (arg.is_arithmetic() && specs.align != align::left) specs.align = align::numeric; @@ -544,7 +544,7 @@ void vprintf(buffer& buf, basic_string_view format, type = 'd'; break; case 'c': - visit_format_arg(char_converter>(arg), arg); + arg.visit(char_converter>(arg)); break; } } @@ -555,7 +555,7 @@ void vprintf(buffer& buf, basic_string_view format, start = it; // Format argument. - visit_format_arg(printf_arg_formatter(out, specs, context), arg); + arg.visit(printf_arg_formatter(out, specs, context)); } write(out, basic_string_view(start, to_unsigned(it - start))); } diff --git a/test/core-test.cc b/test/core-test.cc index 1d84ffd7..b505e2ab 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -273,7 +273,8 @@ struct custom_context { bool called = false; template struct formatter_type { - FMT_CONSTEXPR auto parse(fmt::format_parse_context& ctx) -> decltype(ctx.begin()) { + FMT_CONSTEXPR auto parse(fmt::format_parse_context& ctx) + -> decltype(ctx.begin()) { return ctx.begin(); } @@ -321,7 +322,9 @@ TEST(arg_test, make_value_with_custom_context) { struct test_result {}; template struct mock_visitor { - template struct result { using type = test_result; }; + template struct result { + using type = test_result; + }; mock_visitor() { ON_CALL(*this, visit(_)).WillByDefault(Return(test_result())); @@ -338,10 +341,14 @@ template struct mock_visitor { } }; -template struct visit_type { using type = T; }; +template struct visit_type { + using type = T; +}; -#define VISIT_TYPE(type_, visit_type_) \ - template <> struct visit_type { using type = visit_type_; } +#define VISIT_TYPE(type_, visit_type_) \ + template <> struct visit_type { \ + using type = visit_type_; \ + } VISIT_TYPE(signed char, int); VISIT_TYPE(unsigned char, unsigned); @@ -362,10 +369,8 @@ VISIT_TYPE(unsigned long, unsigned long long); EXPECT_CALL(visitor, visit(expected)); \ using iterator = std::back_insert_iterator>; \ auto var = value; \ - fmt::visit_format_arg( \ - visitor, \ - fmt::detail::make_arg>( \ - var)); \ + fmt::detail::make_arg>(var) \ + .visit(visitor); \ } #define CHECK_ARG_SIMPLE(value) \ @@ -456,14 +461,13 @@ TEST(arg_test, custom_arg) { mock_visitor::handle>; auto&& v = testing::StrictMock(); EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom())); - fmt::visit_format_arg(v, fmt::detail::make_arg(test)); + fmt::detail::make_arg(test).visit(v); } TEST(arg_test, visit_invalid_arg) { auto&& visitor = testing::StrictMock>(); EXPECT_CALL(visitor, visit(_)); - auto arg = fmt::basic_format_arg(); - fmt::visit_format_arg(visitor, arg); + fmt::basic_format_arg().visit(visitor); } #if FMT_USE_CONSTEXPR