mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-09 21:00:06 +00:00
Split standard context into a separate class and optimize
This commit is contained in:
parent
23e8109d8d
commit
a5ae9ae19d
@ -1099,6 +1099,7 @@ 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_args;
|
||||
FMT_EXPORT template <typename Context, typename... Args> class format_arg_store;
|
||||
FMT_EXPORT template <typename Context> class dynamic_format_arg_store;
|
||||
|
||||
// A formatter for objects of type T.
|
||||
@ -1129,7 +1130,7 @@ template <typename T> class basic_appender {
|
||||
using difference_type = ptrdiff_t;
|
||||
FMT_UNCHECKED_ITERATOR(basic_appender);
|
||||
|
||||
basic_appender(detail::buffer<T>& buf) : buffer_(&buf) {}
|
||||
FMT_CONSTEXPR basic_appender(detail::buffer<T>& buf) : buffer_(&buf) {}
|
||||
|
||||
auto operator=(T c) -> basic_appender& {
|
||||
buffer_->push_back(c);
|
||||
@ -1733,39 +1734,158 @@ FMT_DEPRECATED FMT_CONSTEXPR FMT_INLINE auto visit_format_arg(
|
||||
return arg.visit(static_cast<Visitor&&>(vis));
|
||||
}
|
||||
|
||||
// Formatting context.
|
||||
template <typename OutputIt, typename Char> class basic_format_context {
|
||||
/**
|
||||
\rst
|
||||
A view of a collection of formatting arguments. To avoid lifetime issues it
|
||||
should only be used as a parameter type in type-erased functions such as
|
||||
``vformat``::
|
||||
|
||||
void vlog(string_view format_str, format_args args); // OK
|
||||
format_args args = make_format_args(); // Error: dangling reference
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context> class basic_format_args {
|
||||
public:
|
||||
using size_type = int;
|
||||
using format_arg = basic_format_arg<Context>;
|
||||
|
||||
private:
|
||||
OutputIt out_;
|
||||
basic_format_args<basic_format_context> args_;
|
||||
// A descriptor that contains information about formatting arguments.
|
||||
// If the number of arguments is less or equal to max_packed_args then
|
||||
// argument types are passed in the descriptor. This reduces binary code size
|
||||
// per formatting function call.
|
||||
unsigned long long desc_;
|
||||
union {
|
||||
// If is_packed() returns true then argument values are stored in values_;
|
||||
// otherwise they are stored in args_. This is done to improve cache
|
||||
// locality and reduce compiled code size since storing larger objects
|
||||
// may require more code (at least on x86-64) even if the same amount of
|
||||
// data is actually copied to stack. It saves ~10% on the bloat test.
|
||||
const detail::value<Context>* values_;
|
||||
const format_arg* args_;
|
||||
};
|
||||
|
||||
constexpr auto is_packed() const -> bool {
|
||||
return (desc_ & detail::is_unpacked_bit) == 0;
|
||||
}
|
||||
constexpr auto has_named_args() const -> bool {
|
||||
return (desc_ & detail::has_named_args_bit) != 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto type(int index) const -> detail::type {
|
||||
int shift = index * detail::packed_arg_bits;
|
||||
unsigned int mask = (1 << detail::packed_arg_bits) - 1;
|
||||
return static_cast<detail::type>((desc_ >> shift) & mask);
|
||||
}
|
||||
|
||||
constexpr FMT_INLINE basic_format_args(unsigned long long desc,
|
||||
const detail::value<Context>* values)
|
||||
: desc_(desc), values_(values) {}
|
||||
constexpr basic_format_args(unsigned long long desc, const format_arg* args)
|
||||
: desc_(desc), args_(args) {}
|
||||
|
||||
public:
|
||||
constexpr basic_format_args() : desc_(0), args_(nullptr) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
constexpr FMT_INLINE basic_format_args(
|
||||
const format_arg_store<Context, Args...>& store)
|
||||
: basic_format_args(format_arg_store<Context, Args...>::desc,
|
||||
store.data_.args()) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from
|
||||
`~fmt::dynamic_format_arg_store`.
|
||||
\endrst
|
||||
*/
|
||||
constexpr FMT_INLINE basic_format_args(
|
||||
const dynamic_format_arg_store<Context>& store)
|
||||
: basic_format_args(store.get_types(), store.data()) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from a dynamic set of arguments.
|
||||
\endrst
|
||||
*/
|
||||
constexpr basic_format_args(const format_arg* args, int count)
|
||||
: basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
|
||||
args) {}
|
||||
|
||||
/** Returns the argument with the specified id. */
|
||||
FMT_CONSTEXPR auto get(int id) const -> format_arg {
|
||||
format_arg arg;
|
||||
if (!is_packed()) {
|
||||
if (id < max_size()) arg = args_[id];
|
||||
return arg;
|
||||
}
|
||||
if (id >= detail::max_packed_args) return arg;
|
||||
arg.type_ = type(id);
|
||||
if (arg.type_ == detail::type::none_type) return arg;
|
||||
arg.value_ = values_[id];
|
||||
return arg;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
auto get(basic_string_view<Char> name) const -> format_arg {
|
||||
int id = get_id(name);
|
||||
return id >= 0 ? get(id) : format_arg();
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR auto get_id(basic_string_view<Char> name) const -> int {
|
||||
if (!has_named_args()) return -1;
|
||||
const auto& named_args =
|
||||
(is_packed() ? values_[-1] : args_[-1].value_).named_args;
|
||||
for (size_t i = 0; i < named_args.size; ++i) {
|
||||
if (named_args.data[i].name == name) return named_args.data[i].id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto max_size() const -> int {
|
||||
unsigned long long max_packed = detail::max_packed_args;
|
||||
return static_cast<int>(is_packed() ? max_packed
|
||||
: desc_ & ~detail::is_unpacked_bit);
|
||||
}
|
||||
};
|
||||
|
||||
// A formatting context.
|
||||
class context {
|
||||
private:
|
||||
appender out_;
|
||||
basic_format_args<context> args_;
|
||||
detail::locale_ref loc_;
|
||||
|
||||
public:
|
||||
using iterator = OutputIt;
|
||||
using format_arg = basic_format_arg<basic_format_context>;
|
||||
using format_args = basic_format_args<basic_format_context>;
|
||||
using parse_context_type = basic_format_parse_context<Char>;
|
||||
template <typename T> using formatter_type = formatter<T, Char>;
|
||||
|
||||
/** The character type for the output. */
|
||||
using char_type = Char;
|
||||
using char_type = char;
|
||||
|
||||
using iterator = appender;
|
||||
using format_arg = basic_format_arg<context>;
|
||||
using format_args = basic_format_args<context>;
|
||||
using parse_context_type = basic_format_parse_context<char>;
|
||||
template <typename T> using formatter_type = formatter<T, char>;
|
||||
|
||||
basic_format_context(basic_format_context&&) = default;
|
||||
basic_format_context(const basic_format_context&) = delete;
|
||||
void operator=(const basic_format_context&) = delete;
|
||||
/**
|
||||
Constructs a ``basic_format_context`` object. References to the arguments
|
||||
are stored in the object so make sure they have appropriate lifetimes.
|
||||
*/
|
||||
constexpr basic_format_context(OutputIt out, format_args ctx_args,
|
||||
detail::locale_ref loc = {})
|
||||
FMT_CONSTEXPR context(iterator out, format_args ctx_args,
|
||||
detail::locale_ref loc = {})
|
||||
: out_(out), args_(ctx_args), loc_(loc) {}
|
||||
context(context&&) = default;
|
||||
context(const context&) = delete;
|
||||
void operator=(const context&) = delete;
|
||||
|
||||
constexpr auto arg(int id) const -> format_arg { return args_.get(id); }
|
||||
FMT_CONSTEXPR auto arg(basic_string_view<Char> name) -> format_arg {
|
||||
return args_.get(name);
|
||||
}
|
||||
FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
|
||||
FMT_CONSTEXPR auto arg(int id) const -> format_arg { return args_.get(id); }
|
||||
auto arg(string_view name) -> format_arg { return args_.get(name); }
|
||||
FMT_CONSTEXPR auto arg_id(string_view name) -> int {
|
||||
return args_.get_id(name);
|
||||
}
|
||||
auto args() const -> const format_args& { return args_; }
|
||||
@ -1777,16 +1897,22 @@ template <typename OutputIt, typename Char> class basic_format_context {
|
||||
FMT_CONSTEXPR auto out() -> iterator { return out_; }
|
||||
|
||||
// Advances the begin iterator to ``it``.
|
||||
void advance_to(iterator it) {
|
||||
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
|
||||
}
|
||||
void advance_to(iterator) {}
|
||||
|
||||
FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
|
||||
};
|
||||
|
||||
template <typename OutputIt, typename Char> class generic_context;
|
||||
|
||||
// Longer aliases for C++20 compatibility.
|
||||
template <typename OutputIt, typename Char>
|
||||
using basic_format_context =
|
||||
conditional_t<std::is_same<OutputIt, appender>::value, context,
|
||||
generic_context<OutputIt, Char>>;
|
||||
using format_context = context;
|
||||
|
||||
template <typename Char>
|
||||
using buffer_context = basic_format_context<basic_appender<Char>, Char>;
|
||||
using format_context = basic_format_context<appender, char>;
|
||||
|
||||
template <typename T, typename Char = char>
|
||||
using is_formattable = bool_constant<!std::is_base_of<
|
||||
@ -1874,127 +2000,6 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
|
||||
}
|
||||
FMT_END_EXPORT
|
||||
|
||||
/**
|
||||
\rst
|
||||
A view of a collection of formatting arguments. To avoid lifetime issues it
|
||||
should only be used as a parameter type in type-erased functions such as
|
||||
``vformat``::
|
||||
|
||||
void vlog(string_view format_str, format_args args); // OK
|
||||
format_args args = make_format_args(); // Error: dangling reference
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context> class basic_format_args {
|
||||
public:
|
||||
using size_type = int;
|
||||
using format_arg = basic_format_arg<Context>;
|
||||
|
||||
private:
|
||||
// A descriptor that contains information about formatting arguments.
|
||||
// If the number of arguments is less or equal to max_packed_args then
|
||||
// argument types are passed in the descriptor. This reduces binary code size
|
||||
// per formatting function call.
|
||||
unsigned long long desc_;
|
||||
union {
|
||||
// If is_packed() returns true then argument values are stored in values_;
|
||||
// otherwise they are stored in args_. This is done to improve cache
|
||||
// locality and reduce compiled code size since storing larger objects
|
||||
// may require more code (at least on x86-64) even if the same amount of
|
||||
// data is actually copied to stack. It saves ~10% on the bloat test.
|
||||
const detail::value<Context>* values_;
|
||||
const format_arg* args_;
|
||||
};
|
||||
|
||||
constexpr auto is_packed() const -> bool {
|
||||
return (desc_ & detail::is_unpacked_bit) == 0;
|
||||
}
|
||||
auto has_named_args() const -> bool {
|
||||
return (desc_ & detail::has_named_args_bit) != 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto type(int index) const -> detail::type {
|
||||
int shift = index * detail::packed_arg_bits;
|
||||
unsigned int mask = (1 << detail::packed_arg_bits) - 1;
|
||||
return static_cast<detail::type>((desc_ >> shift) & mask);
|
||||
}
|
||||
|
||||
constexpr FMT_INLINE basic_format_args(unsigned long long desc,
|
||||
const detail::value<Context>* values)
|
||||
: desc_(desc), values_(values) {}
|
||||
constexpr basic_format_args(unsigned long long desc, const format_arg* args)
|
||||
: desc_(desc), args_(args) {}
|
||||
|
||||
public:
|
||||
constexpr basic_format_args() : desc_(0), args_(nullptr) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from `~fmt::format_arg_store`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
constexpr FMT_INLINE basic_format_args(
|
||||
const format_arg_store<Context, Args...>& store)
|
||||
: basic_format_args(format_arg_store<Context, Args...>::desc,
|
||||
store.data_.args()) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from
|
||||
`~fmt::dynamic_format_arg_store`.
|
||||
\endrst
|
||||
*/
|
||||
constexpr FMT_INLINE basic_format_args(
|
||||
const dynamic_format_arg_store<Context>& store)
|
||||
: basic_format_args(store.get_types(), store.data()) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a `basic_format_args` object from a dynamic set of arguments.
|
||||
\endrst
|
||||
*/
|
||||
constexpr basic_format_args(const format_arg* args, int count)
|
||||
: basic_format_args(detail::is_unpacked_bit | detail::to_unsigned(count),
|
||||
args) {}
|
||||
|
||||
/** Returns the argument with the specified id. */
|
||||
FMT_CONSTEXPR auto get(int id) const -> format_arg {
|
||||
format_arg arg;
|
||||
if (!is_packed()) {
|
||||
if (id < max_size()) arg = args_[id];
|
||||
return arg;
|
||||
}
|
||||
if (id >= detail::max_packed_args) return arg;
|
||||
arg.type_ = type(id);
|
||||
if (arg.type_ == detail::type::none_type) return arg;
|
||||
arg.value_ = values_[id];
|
||||
return arg;
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
auto get(basic_string_view<Char> name) const -> format_arg {
|
||||
int id = get_id(name);
|
||||
return id >= 0 ? get(id) : format_arg();
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
auto get_id(basic_string_view<Char> name) const -> int {
|
||||
if (!has_named_args()) return -1;
|
||||
const auto& named_args =
|
||||
(is_packed() ? values_[-1] : args_[-1].value_).named_args;
|
||||
for (size_t i = 0; i < named_args.size; ++i) {
|
||||
if (named_args.data[i].name == name) return named_args.data[i].id;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto max_size() const -> int {
|
||||
unsigned long long max_packed = detail::max_packed_args;
|
||||
return static_cast<int>(is_packed() ? max_packed
|
||||
: desc_ & ~detail::is_unpacked_bit);
|
||||
}
|
||||
};
|
||||
|
||||
/** An alias to ``basic_format_args<format_context>``. */
|
||||
// A separate type would result in shorter symbols but break ABI compatibility
|
||||
// between clang and gcc on ARM (#1919).
|
||||
|
@ -1071,6 +1071,50 @@ constexpr auto compile_string_to_view(detail::std_string_view<Char> s)
|
||||
}
|
||||
} // namespace detail_exported
|
||||
|
||||
// A generic formatting context with custom output iterator and character
|
||||
// (code unit) support.
|
||||
template <typename OutputIt, typename Char> class generic_context {
|
||||
private:
|
||||
OutputIt out_;
|
||||
basic_format_args<generic_context> args_;
|
||||
detail::locale_ref loc_;
|
||||
|
||||
public:
|
||||
using char_type = Char;
|
||||
using iterator = OutputIt;
|
||||
using format_args = basic_format_args<generic_context>;
|
||||
using parse_context_type = basic_format_parse_context<Char>;
|
||||
template <typename T> using formatter_type = formatter<T, Char>;
|
||||
|
||||
constexpr generic_context(OutputIt out, format_args ctx_args,
|
||||
detail::locale_ref loc = {})
|
||||
: out_(out), args_(ctx_args), loc_(loc) {}
|
||||
generic_context(generic_context&&) = default;
|
||||
generic_context(const generic_context&) = delete;
|
||||
void operator=(const generic_context&) = delete;
|
||||
|
||||
constexpr auto arg(int id) const -> basic_format_arg<generic_context> {
|
||||
return args_.get(id);
|
||||
}
|
||||
auto arg(basic_string_view<Char> name) -> basic_format_arg<generic_context> {
|
||||
return args_.get(name);
|
||||
}
|
||||
FMT_CONSTEXPR auto arg_id(basic_string_view<Char> name) -> int {
|
||||
return args_.get_id(name);
|
||||
}
|
||||
auto args() const -> const format_args& { return args_; }
|
||||
|
||||
void on_error(const char* message) { throw_format_error(message); }
|
||||
|
||||
FMT_CONSTEXPR auto out() -> iterator { return out_; }
|
||||
|
||||
void advance_to(iterator it) {
|
||||
if (!detail::is_back_insert_iterator<iterator>()) out_ = it;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
|
||||
};
|
||||
|
||||
class loc_value {
|
||||
private:
|
||||
basic_format_arg<format_context> value_;
|
||||
|
@ -143,9 +143,8 @@ template <typename Char>
|
||||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
||||
void set_debug_format() = delete;
|
||||
|
||||
template <typename T, typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
|
||||
-> OutputIt {
|
||||
template <typename T, typename Context>
|
||||
auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::format_value(buffer, value);
|
||||
return formatter<basic_string_view<Char>, Char>::format(
|
||||
@ -158,9 +157,9 @@ using ostream_formatter = basic_ostream_formatter<char>;
|
||||
template <typename T, typename Char>
|
||||
struct formatter<detail::streamed_view<T>, Char>
|
||||
: basic_ostream_formatter<Char> {
|
||||
template <typename OutputIt>
|
||||
auto format(detail::streamed_view<T> view,
|
||||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||
template <typename Context>
|
||||
auto format(detail::streamed_view<T> view, Context& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return basic_ostream_formatter<Char>::format(view.value, ctx);
|
||||
}
|
||||
};
|
||||
|
@ -400,9 +400,9 @@ struct formatter<
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const std::exception& ex,
|
||||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||
template <typename Context>
|
||||
auto format(const std::exception& ex, Context& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
format_specs<Char> spec;
|
||||
auto out = ctx.out();
|
||||
if (!with_typename_)
|
||||
|
@ -368,7 +368,7 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
||||
{ \
|
||||
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
|
||||
EXPECT_CALL(visitor, visit(expected)); \
|
||||
using iterator = std::back_insert_iterator<buffer<Char>>; \
|
||||
using iterator = fmt::basic_appender<Char>; \
|
||||
auto var = value; \
|
||||
fmt::detail::make_arg<fmt::basic_format_context<iterator, Char>>(var) \
|
||||
.visit(visitor); \
|
||||
@ -379,7 +379,6 @@ VISIT_TYPE(unsigned long, unsigned long long);
|
||||
using value_type = decltype(value); \
|
||||
typename visit_type<value_type>::type expected = value; \
|
||||
CHECK_ARG(char, expected, value) \
|
||||
CHECK_ARG(wchar_t, expected, value) \
|
||||
}
|
||||
|
||||
template <typename T> class numeric_arg_test : public testing::Test {};
|
||||
@ -419,23 +418,10 @@ TEST(arg_test, string_arg) {
|
||||
CHECK_ARG(char, sv, std::string(str));
|
||||
}
|
||||
|
||||
TEST(arg_test, wstring_arg) {
|
||||
wchar_t str_data[] = L"test";
|
||||
wchar_t* str = str_data;
|
||||
const wchar_t* cstr = str;
|
||||
|
||||
auto sv = fmt::basic_string_view<wchar_t>(str);
|
||||
CHECK_ARG(wchar_t, cstr, str);
|
||||
CHECK_ARG(wchar_t, cstr, cstr);
|
||||
CHECK_ARG(wchar_t, sv, std::wstring(str));
|
||||
CHECK_ARG(wchar_t, sv, fmt::basic_string_view<wchar_t>(str));
|
||||
}
|
||||
|
||||
TEST(arg_test, pointer_arg) {
|
||||
void* p = nullptr;
|
||||
const void* cp = nullptr;
|
||||
CHECK_ARG(char, cp, p);
|
||||
CHECK_ARG(wchar_t, cp, p);
|
||||
CHECK_ARG_SIMPLE(cp);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user