Cleanup argument construction
This commit is contained in:
parent
d7592ad8bf
commit
ea49c91cd1
@ -1301,9 +1301,6 @@ template <typename Context> class value {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Context, typename T>
|
|
||||||
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context>;
|
|
||||||
|
|
||||||
// To minimize the number of types we need to deal with, long is translated
|
// 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.
|
// either to int or to long long depending on its size.
|
||||||
enum { long_short = sizeof(long) == sizeof(int) };
|
enum { long_short = sizeof(long) == sizeof(int) };
|
||||||
@ -1497,110 +1494,6 @@ class appender : public std::back_insert_iterator<detail::buffer<char>> {
|
|||||||
auto operator++(int) noexcept -> appender { return *this; }
|
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 <typename Context> class basic_format_arg {
|
|
||||||
private:
|
|
||||||
detail::value<Context> value_;
|
|
||||||
detail::type type_;
|
|
||||||
|
|
||||||
template <typename ContextType, typename T>
|
|
||||||
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>;
|
|
||||||
|
|
||||||
using char_type = typename Context::char_type;
|
|
||||||
|
|
||||||
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
|
||||||
friend struct detail::arg_data;
|
|
||||||
|
|
||||||
basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
|
|
||||||
: value_(args, size) {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
class handle {
|
|
||||||
public:
|
|
||||||
explicit handle(detail::custom_value<Context> 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<Context> 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 <typename Visitor, typename Context>
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename Char, typename InputIt>
|
template <typename Char, typename InputIt>
|
||||||
@ -1695,30 +1588,128 @@ FMT_CONSTEXPR FMT_INLINE auto make_value(T&& val) -> value<Context> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Context, typename T>
|
template <typename Context, typename T>
|
||||||
FMT_CONSTEXPR auto make_arg(T&& value) -> basic_format_arg<Context> {
|
FMT_CONSTEXPR auto make_arg(T& val) -> basic_format_arg<Context> {
|
||||||
auto arg = basic_format_arg<Context>();
|
auto arg = basic_format_arg<Context>();
|
||||||
arg.type_ = mapped_type_constant<T, Context>::value;
|
arg.type_ = mapped_type_constant<T, Context>::value;
|
||||||
arg.value_ = make_value<Context>(value);
|
arg.value_ = make_value<Context>(val);
|
||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The DEPRECATED type template parameter is there to avoid an ODR violation
|
template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(PACKED)>
|
||||||
// when using a fallback formatter in one translation unit and an implicit
|
FMT_CONSTEXPR FMT_INLINE auto make_arg(T& val) -> value<Context> {
|
||||||
// conversion in another (not recommended).
|
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
|
||||||
FMT_ENABLE_IF(IS_PACKED)>
|
|
||||||
FMT_CONSTEXPR FMT_INLINE auto make_arg(T&& val) -> value<Context> {
|
|
||||||
return make_value<Context>(val);
|
return make_value<Context>(val);
|
||||||
}
|
}
|
||||||
|
template <bool PACKED, typename Context, typename T, FMT_ENABLE_IF(!PACKED)>
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
|
||||||
FMT_ENABLE_IF(!IS_PACKED)>
|
return make_arg<Context>(val);
|
||||||
FMT_CONSTEXPR inline auto make_arg(T&& value) -> basic_format_arg<Context> {
|
|
||||||
return make_arg<Context>(value);
|
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
FMT_BEGIN_EXPORT
|
FMT_BEGIN_EXPORT
|
||||||
|
|
||||||
|
// A formatting argument. It is a trivially copyable/constructible type to
|
||||||
|
// allow storage in basic_memory_buffer.
|
||||||
|
template <typename Context> class basic_format_arg {
|
||||||
|
private:
|
||||||
|
detail::value<Context> value_;
|
||||||
|
detail::type type_;
|
||||||
|
|
||||||
|
template <typename ContextType, typename T>
|
||||||
|
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>;
|
||||||
|
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
||||||
|
friend struct detail::arg_data;
|
||||||
|
|
||||||
|
basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
|
||||||
|
: value_(args, size) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class handle {
|
||||||
|
public:
|
||||||
|
explicit handle(detail::custom_value<Context> 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<Context> 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 <typename Visitor, typename Context>
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
// Formatting context.
|
// Formatting context.
|
||||||
template <typename OutputIt, typename Char> class basic_format_context {
|
template <typename OutputIt, typename Char> class basic_format_context {
|
||||||
private:
|
private:
|
||||||
@ -1822,9 +1813,7 @@ class format_arg_store
|
|||||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||||
basic_format_args<Context>(*this),
|
basic_format_args<Context>(*this),
|
||||||
#endif
|
#endif
|
||||||
data_{detail::make_arg<
|
data_{detail::make_arg<is_packed, Context>(args)...} {
|
||||||
is_packed, Context,
|
|
||||||
detail::mapped_type_constant<Args, Context>::value>(args)...} {
|
|
||||||
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
detail::init_named_args(data_.named_args(), 0, 0, args...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -132,22 +132,23 @@ template <typename T, typename Context> class arg_converter {
|
|||||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||||
// Extra casts are used to silence warnings.
|
// Extra casts are used to silence warnings.
|
||||||
if (is_signed) {
|
if (is_signed) {
|
||||||
arg_ = detail::make_arg<Context>(
|
auto n = static_cast<int>(static_cast<target_type>(value));
|
||||||
static_cast<int>(static_cast<target_type>(value)));
|
arg_ = detail::make_arg<Context>(n);
|
||||||
} else {
|
} else {
|
||||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||||
arg_ = detail::make_arg<Context>(
|
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
|
||||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
arg_ = detail::make_arg<Context>(n);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (is_signed) {
|
if (is_signed) {
|
||||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
// std::printf("%lld", -42); // prints "4294967254"
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
// but we don't have to do the same because it's a UB.
|
// but we don't have to do the same because it's a UB.
|
||||||
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
auto n = static_cast<long long>(value);
|
||||||
|
arg_ = detail::make_arg<Context>(n);
|
||||||
} else {
|
} else {
|
||||||
arg_ = detail::make_arg<Context>(
|
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
|
||||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
arg_ = detail::make_arg<Context>(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,8 +176,8 @@ template <typename Context> class char_converter {
|
|||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
void operator()(T value) {
|
void operator()(T value) {
|
||||||
arg_ = detail::make_arg<Context>(
|
auto c = static_cast<typename Context::char_type>(value);
|
||||||
static_cast<typename Context::char_type>(value));
|
arg_ = detail::make_arg<Context>(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
@ -476,9 +477,9 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
auto str = visit_format_arg(get_cstring<Char>(), arg);
|
auto str = visit_format_arg(get_cstring<Char>(), arg);
|
||||||
auto str_end = str + specs.precision;
|
auto str_end = str + specs.precision;
|
||||||
auto nul = std::find(str, str_end, Char());
|
auto nul = std::find(str, str_end, Char());
|
||||||
arg = make_arg<basic_printf_context<iterator, Char>>(
|
auto sv = basic_string_view<Char>(
|
||||||
basic_string_view<Char>(
|
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
|
||||||
str, to_unsigned(nul != str_end ? nul - str : specs.precision)));
|
arg = make_arg<basic_printf_context<iterator, Char>>(sv);
|
||||||
}
|
}
|
||||||
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
|
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
|
||||||
if (specs.fill[0] == '0') {
|
if (specs.fill[0] == '0') {
|
||||||
|
@ -361,10 +361,11 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
|||||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||||
EXPECT_CALL(visitor, visit(expected)); \
|
EXPECT_CALL(visitor, visit(expected)); \
|
||||||
using iterator = std::back_insert_iterator<buffer<Char>>; \
|
using iterator = std::back_insert_iterator<buffer<Char>>; \
|
||||||
|
auto var = value; \
|
||||||
fmt::visit_format_arg( \
|
fmt::visit_format_arg( \
|
||||||
visitor, \
|
visitor, \
|
||||||
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \
|
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>( \
|
||||||
value)); \
|
var)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK_ARG_SIMPLE(value) \
|
#define CHECK_ARG_SIMPLE(value) \
|
||||||
|
@ -1773,20 +1773,6 @@ TEST(format_test, variadic) {
|
|||||||
EXPECT_EQ("abc1", fmt::format("{}c{}", "ab", 1));
|
EXPECT_EQ("abc1", fmt::format("{}c{}", "ab", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(format_test, dynamic) {
|
|
||||||
using ctx = fmt::format_context;
|
|
||||||
auto args = std::vector<fmt::basic_format_arg<ctx>>();
|
|
||||||
args.emplace_back(fmt::detail::make_arg<ctx>(42));
|
|
||||||
args.emplace_back(fmt::detail::make_arg<ctx>("abc1"));
|
|
||||||
args.emplace_back(fmt::detail::make_arg<ctx>(1.5f));
|
|
||||||
|
|
||||||
std::string result = fmt::vformat(
|
|
||||||
"{} and {} and {}",
|
|
||||||
fmt::format_args(args.data(), static_cast<int>(args.size())));
|
|
||||||
|
|
||||||
EXPECT_EQ("42 and abc1 and 1.5", result);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(format_test, bytes) {
|
TEST(format_test, bytes) {
|
||||||
auto s = fmt::format("{:10}", fmt::bytes("ёжик"));
|
auto s = fmt::format("{:10}", fmt::bytes("ёжик"));
|
||||||
EXPECT_EQ("ёжик ", s);
|
EXPECT_EQ("ёжик ", s);
|
||||||
@ -2098,8 +2084,8 @@ TEST(format_test, format_to_n_output_iterator) {
|
|||||||
|
|
||||||
TEST(format_test, vformat_to) {
|
TEST(format_test, vformat_to) {
|
||||||
using context = fmt::format_context;
|
using context = fmt::format_context;
|
||||||
fmt::basic_format_arg<context> arg = fmt::detail::make_arg<context>(42);
|
int n = 42;
|
||||||
auto args = fmt::basic_format_args<context>(&arg, 1);
|
auto args = fmt::make_format_args<context>(n);
|
||||||
auto s = std::string();
|
auto s = std::string();
|
||||||
fmt::vformat_to(std::back_inserter(s), "{}", args);
|
fmt::vformat_to(std::back_inserter(s), "{}", args);
|
||||||
EXPECT_EQ("42", s);
|
EXPECT_EQ("42", s);
|
||||||
|
@ -146,14 +146,9 @@ TEST(xchar_test, format_to) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(xchar_test, vformat_to) {
|
TEST(xchar_test, vformat_to) {
|
||||||
using wcontext = fmt::wformat_context;
|
auto args = fmt::make_wformat_args(42);
|
||||||
fmt::basic_format_arg<wcontext> warg = fmt::detail::make_arg<wcontext>(42);
|
|
||||||
auto wargs = fmt::basic_format_args<wcontext>(&warg, 1);
|
|
||||||
auto w = std::wstring();
|
auto w = std::wstring();
|
||||||
fmt::vformat_to(std::back_inserter(w), L"{}", wargs);
|
fmt::vformat_to(std::back_inserter(w), L"{}", args);
|
||||||
EXPECT_EQ(L"42", w);
|
|
||||||
w.clear();
|
|
||||||
fmt::vformat_to(std::back_inserter(w), FMT_STRING(L"{}"), wargs);
|
|
||||||
EXPECT_EQ(L"42", w);
|
EXPECT_EQ(L"42", w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user