Improve arg storage

This commit is contained in:
Victor Zverovich 2024-01-08 20:40:03 -08:00
parent 6af30d8f75
commit fb66131efa
4 changed files with 60 additions and 55 deletions

View File

@ -1099,7 +1099,8 @@ FMT_CONSTEXPR void basic_format_parse_context<Char>::check_dynamic_spec(
FMT_EXPORT template <typename Context> class basic_format_arg; FMT_EXPORT template <typename Context> class basic_format_arg;
FMT_EXPORT template <typename Context> class basic_format_args; FMT_EXPORT template <typename Context> class basic_format_args;
FMT_EXPORT template <typename, size_t, typename...> class format_arg_store_impl; FMT_EXPORT template <typename, size_t, size_t, unsigned long long>
struct format_arg_store_impl;
FMT_EXPORT template <typename Context> class dynamic_format_arg_store; FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
// A formatter for objects of type T. // A formatter for objects of type T.
@ -1611,7 +1612,8 @@ template <typename Context> class basic_format_arg {
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
template <typename, size_t, typename...> friend class format_arg_store_impl; template <typename, size_t, size_t, unsigned long long>
friend struct format_arg_store_impl;
basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size) basic_format_arg(const detail::named_arg_info<char_type>* args, size_t size)
: value_(args, size) {} : value_(args, size) {}
@ -1758,19 +1760,19 @@ template <typename Context> class basic_format_args {
Constructs a `basic_format_args` object from `~fmt::format_arg_store`. Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
\endrst \endrst
*/ */
template <size_t N, typename... T, template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
FMT_ENABLE_IF(sizeof...(T) <= detail::max_packed_args)> FMT_ENABLE_IF(NUM_ARGS <= detail::max_packed_args)>
constexpr basic_format_args( constexpr basic_format_args(
const format_arg_store_impl<Context, N, T...>& store) const format_arg_store_impl<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>&
: desc_(format_arg_store_impl<Context, N, T...>::desc), store)
values_(store.args()) {} : desc_(DESC), values_(store.args()) {}
template <size_t N, typename... T, template <size_t NUM_ARGS, size_t NUM_NAMED_ARGS, unsigned long long DESC,
FMT_ENABLE_IF(sizeof...(T) > detail::max_packed_args)> FMT_ENABLE_IF(NUM_ARGS > detail::max_packed_args)>
constexpr basic_format_args( constexpr basic_format_args(
const format_arg_store_impl<Context, N, T...>& store) const format_arg_store_impl<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC>&
: desc_(format_arg_store_impl<Context, N, T...>::desc), store)
args_(store.args()) {} : desc_(DESC), args_(store.args()) {}
/** /**
\rst \rst
@ -1900,72 +1902,48 @@ using is_formattable = bool_constant<!std::is_base_of<
such as `~fmt::vformat`. such as `~fmt::vformat`.
\endrst \endrst
*/ */
template <typename Context, size_t NUM_NAMED_ARGS, typename... Args> template <typename Context, size_t NUM_ARGS, size_t NUM_NAMED_ARGS,
class format_arg_store_impl { unsigned long long DESC>
private: struct format_arg_store_impl {
static const size_t num_args = sizeof...(Args); static const bool is_packed = NUM_ARGS <= detail::max_packed_args;
static const bool is_packed = num_args <= detail::max_packed_args;
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
using value_type = conditional_t<is_packed, detail::value<Context>, using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>; basic_format_arg<Context>>;
// args_[0].named_args points to named_args_ to avoid bloating format_args. // args_[0].named_args points to named_args to avoid bloating format_args.
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
value_type args_[1 + (num_args != 0 ? num_args : +1)]; value_type args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
detail::named_arg_info<char_type> named_args_[NUM_NAMED_ARGS]; detail::named_arg_info<char_type> named_args[NUM_NAMED_ARGS];
public:
template <typename... T> template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store_impl(T&... args) FMT_CONSTEXPR FMT_INLINE format_arg_store_impl(T&... args)
: args_{value_type(named_args_, NUM_NAMED_ARGS), : args_{value_type(named_args, NUM_NAMED_ARGS),
detail::make_arg<is_packed, Context>(args)...} { detail::make_arg<is_packed, Context>(args)...} {
detail::init_named_args(named_args_, 0, 0, args...); detail::init_named_args(named_args, 0, 0, args...);
} }
static constexpr unsigned long long desc =
(is_packed ? detail::encode_types<Context, Args...>()
: detail::is_unpacked_bit | num_args) |
(NUM_NAMED_ARGS != 0
? static_cast<unsigned long long>(detail::has_named_args_bit)
: 0);
FMT_CONSTEXPR FMT_INLINE auto args() const -> const value_type* { FMT_CONSTEXPR FMT_INLINE auto args() const -> const value_type* {
return args_ + 1; return args_ + 1;
} }
}; };
// A specialization of format_arg_store_impl without named arguments. // A specialization of format_arg_store_impl without named arguments.
template <typename Context, typename... Args> template <typename Context, size_t NUM_ARGS, unsigned long long DESC>
class format_arg_store_impl<Context, 0, Args...> { struct format_arg_store_impl<Context, NUM_ARGS, 0, DESC> {
private: static const bool is_packed = NUM_ARGS <= detail::max_packed_args;
static const size_t num_args = sizeof...(Args);
static const bool is_packed = num_args <= detail::max_packed_args;
using value_type = conditional_t<is_packed, detail::value<Context>, using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>; basic_format_arg<Context>>;
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
value_type args_[num_args != 0 ? num_args : +1]; value_type args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
public:
template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store_impl(T&... args)
: args_{detail::make_arg<is_packed, Context>(args)...} {}
static constexpr unsigned long long desc =
(is_packed ? detail::encode_types<Context, Args...>()
: detail::is_unpacked_bit | num_args);
FMT_CONSTEXPR FMT_INLINE auto args() const -> const value_type* { FMT_CONSTEXPR FMT_INLINE auto args() const -> const value_type* {
return args_; return args_;
} }
}; };
template <typename Context, typename... T>
using format_arg_store =
format_arg_store_impl<Context, detail::count_named_args<T...>(), T...>;
/** /**
\rst \rst
Constructs an object that stores references to arguments and can be implicitly Constructs an object that stores references to arguments and can be implicitly
@ -1978,12 +1956,38 @@ using format_arg_store =
// auto args = make_format_args(std::string()); // auto args = make_format_args(std::string());
// Use format_arg_store_impl instead of format_arg_store for shorter symbols. // Use format_arg_store_impl instead of format_arg_store for shorter symbols.
template <typename Context = format_context, typename... T, template <typename Context = format_context, typename... T,
size_t N = detail::count_named_args<T...>()> size_t NUM_ARGS = sizeof...(T),
size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
unsigned long long DESC = NUM_ARGS <= detail::max_packed_args
? detail::encode_types<Context, T...>()
: detail::is_unpacked_bit | NUM_ARGS,
FMT_ENABLE_IF(NUM_NAMED_ARGS == 0)>
constexpr auto make_format_args(T&... args) constexpr auto make_format_args(T&... args)
-> format_arg_store_impl<Context, N, remove_const_t<T>...> { -> format_arg_store_impl<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
return {{detail::make_arg<NUM_ARGS <= detail::max_packed_args, Context>(
args)...}};
}
template <typename Context = format_context, typename... T,
size_t NUM_ARGS = sizeof...(T),
size_t NUM_NAMED_ARGS = detail::count_named_args<T...>(),
unsigned long long DESC =
(NUM_ARGS <= detail::max_packed_args
? detail::encode_types<Context, T...>()
: detail::is_unpacked_bit | NUM_ARGS) |
(NUM_NAMED_ARGS != 0
? static_cast<unsigned long long>(detail::has_named_args_bit)
: 0),
FMT_ENABLE_IF(NUM_NAMED_ARGS != 0)>
constexpr auto make_format_args(T&... args)
-> format_arg_store_impl<Context, NUM_ARGS, NUM_NAMED_ARGS, DESC> {
return {args...}; return {args...};
} }
template <typename Context, typename... T>
using format_arg_store =
decltype(make_format_args<Context>(std::declval<T&>()...));
/** /**
\rst \rst
Returns a named argument to be used in a formatting function. Returns a named argument to be used in a formatting function.

View File

@ -87,9 +87,9 @@ template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {}; template <> struct is_char<char32_t> : std::true_type {};
template <typename... T> template <typename... T>
constexpr auto make_wformat_args(const T&... args) constexpr auto make_wformat_args(T&... args)
-> format_arg_store<wformat_context, T...> { -> format_arg_store<wformat_context, T...> {
return {args...}; return make_format_args<wformat_context>(args...);
} }
inline namespace literals { inline namespace literals {

View File

@ -529,7 +529,7 @@ TEST(printf_test, wide_string) {
TEST(printf_test, vprintf) { TEST(printf_test, vprintf) {
int n = 42; int n = 42;
auto store = fmt::format_arg_store<fmt::printf_context, int>(n); auto store = fmt::make_format_args<fmt::printf_context>(n);
auto args = fmt::basic_format_args<fmt::printf_context>(store); auto args = fmt::basic_format_args<fmt::printf_context>(store);
EXPECT_EQ(fmt::vsprintf(fmt::string_view("%d"), args), "42"); EXPECT_EQ(fmt::vsprintf(fmt::string_view("%d"), args), "42");
EXPECT_WRITE(stdout, fmt::vfprintf(stdout, fmt::string_view("%d"), args), EXPECT_WRITE(stdout, fmt::vfprintf(stdout, fmt::string_view("%d"), args),

View File

@ -194,7 +194,8 @@ TEST(xchar_test, format_to) {
} }
TEST(xchar_test, vformat_to) { TEST(xchar_test, vformat_to) {
auto args = fmt::make_wformat_args(42); int n = 42;
auto args = fmt::make_wformat_args(n);
auto w = std::wstring(); auto w = std::wstring();
fmt::vformat_to(std::back_inserter(w), L"{}", args); fmt::vformat_to(std::back_inserter(w), L"{}", args);
EXPECT_EQ(L"42", w); EXPECT_EQ(L"42", w);