Make visitation compatible with std::format
This commit is contained in:
parent
50565f9853
commit
068bf9bad8
@ -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 <typename Context> class basic_format_arg {
|
||||
friend FMT_CONSTEXPR auto detail::make_arg(T& value)
|
||||
-> basic_format_arg<ContextType>;
|
||||
|
||||
template <typename Visitor, typename Ctx>
|
||||
friend FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
|
||||
const basic_format_arg<Ctx>& arg)
|
||||
-> decltype(vis(0));
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
friend class dynamic_format_arg_store<Context>;
|
||||
|
||||
@ -1667,6 +1676,53 @@ template <typename Context> 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 <typename Visitor>
|
||||
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<typename Context::char_type>;
|
||||
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<Context>::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 <typename Context> 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 <typename Visitor, typename Context>
|
||||
FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
|
||||
FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
|
||||
Visitor&& vis, const basic_format_arg<Context>& 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<typename Context::char_type>;
|
||||
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<Context>::handle(arg.value_.custom));
|
||||
}
|
||||
return vis(monostate());
|
||||
return arg.visit(std::forward<Visitor>(vis));
|
||||
}
|
||||
|
||||
// Formatting context.
|
||||
|
@ -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 <typename Visitor> 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 <typename Handler, typename FormatArg>
|
||||
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<int>()))
|
||||
throw_format_error("number is too big");
|
||||
return static_cast<int>(value);
|
||||
@ -4278,7 +4264,7 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> 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<Char>{out, args, loc}, arg);
|
||||
arg.visit(default_arg_formatter<Char>{out, args, loc});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -4310,10 +4296,8 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> 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<Char>{context.out(), context.args(),
|
||||
context.locale()},
|
||||
arg));
|
||||
context.advance_to(arg.visit(default_arg_formatter<Char>{
|
||||
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<Char>& buf, basic_string_view<Char> fmt,
|
||||
specs.precision, specs.precision_ref, context);
|
||||
if (begin == end || *begin != '}')
|
||||
throw_format_error("missing '}' in format string");
|
||||
auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
|
||||
context.advance_to(visit_format_arg(f, arg));
|
||||
context.advance_to(arg.visit(
|
||||
arg_formatter<Char>{context.out(), specs, context.locale()}));
|
||||
return begin;
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ template <typename T, typename Context> class arg_converter {
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||
arg.visit(arg_converter<T, Context>(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<Char>& specs,
|
||||
if (specs.width == -1) throw_format_error("number is too big");
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||
specs.width = static_cast<int>(
|
||||
get_arg(-1).visit(detail::printf_width_handler<Char>(specs)));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
@ -462,8 +462,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(printf_precision_handler(), get_arg(-1)));
|
||||
specs.precision =
|
||||
static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
@ -477,14 +477,14 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
specs.fill[0] = ' ';
|
||||
}
|
||||
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
|
||||
auto str = visit_format_arg(get_cstring<Char>(), arg);
|
||||
auto str = arg.visit(get_cstring<Char>());
|
||||
auto str_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
auto sv = basic_string_view<Char>(
|
||||
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
|
||||
arg = make_arg<basic_printf_context<Char>>(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<Char>& buf, basic_string_view<Char> format,
|
||||
type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
|
||||
arg.visit(char_converter<basic_printf_context<Char>>(arg));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -555,7 +555,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
|
||||
arg.visit(printf_arg_formatter<Char>(out, specs, context));
|
||||
}
|
||||
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||
}
|
||||
|
@ -273,7 +273,8 @@ struct custom_context {
|
||||
bool called = false;
|
||||
|
||||
template <typename T> 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 <typename T> struct mock_visitor {
|
||||
template <typename U> struct result { using type = test_result; };
|
||||
template <typename U> struct result {
|
||||
using type = test_result;
|
||||
};
|
||||
|
||||
mock_visitor() {
|
||||
ON_CALL(*this, visit(_)).WillByDefault(Return(test_result()));
|
||||
@ -338,10 +341,14 @@ template <typename T> struct mock_visitor {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct visit_type { using type = T; };
|
||||
template <typename T> struct visit_type {
|
||||
using type = T;
|
||||
};
|
||||
|
||||
#define VISIT_TYPE(type_, visit_type_) \
|
||||
template <> struct visit_type<type_> { using type = visit_type_; }
|
||||
#define VISIT_TYPE(type_, visit_type_) \
|
||||
template <> struct visit_type<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<buffer<Char>>; \
|
||||
auto var = value; \
|
||||
fmt::visit_format_arg( \
|
||||
visitor, \
|
||||
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \
|
||||
var)); \
|
||||
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>(var) \
|
||||
.visit(visitor); \
|
||||
}
|
||||
|
||||
#define CHECK_ARG_SIMPLE(value) \
|
||||
@ -456,14 +461,13 @@ TEST(arg_test, custom_arg) {
|
||||
mock_visitor<fmt::basic_format_arg<fmt::format_context>::handle>;
|
||||
auto&& v = testing::StrictMock<visitor>();
|
||||
EXPECT_CALL(v, visit(_)).WillOnce(Invoke(check_custom()));
|
||||
fmt::visit_format_arg(v, fmt::detail::make_arg<fmt::format_context>(test));
|
||||
fmt::detail::make_arg<fmt::format_context>(test).visit(v);
|
||||
}
|
||||
|
||||
TEST(arg_test, visit_invalid_arg) {
|
||||
auto&& visitor = testing::StrictMock<mock_visitor<fmt::monostate>>();
|
||||
EXPECT_CALL(visitor, visit(_));
|
||||
auto arg = fmt::basic_format_arg<fmt::format_context>();
|
||||
fmt::visit_format_arg(visitor, arg);
|
||||
fmt::basic_format_arg<fmt::format_context>().visit(visitor);
|
||||
}
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
|
Loading…
Reference in New Issue
Block a user