Skip to content

Commit

Permalink
Split standard context into a separate class and optimize
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed Jan 8, 2024
1 parent 23e8109 commit a5ae9ae
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 168 deletions.
293 changes: 149 additions & 144 deletions include/fmt/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -1733,147 +1734,6 @@ 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 {
private:
OutputIt out_;
basic_format_args<basic_format_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;

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 = {})
: out_(out), args_(ctx_args), loc_(loc) {}

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 {
return args_.get_id(name);
}
auto args() const -> const format_args& { return args_; }

// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char* message) { throw_format_error(message); }

// Returns an iterator to the beginning of the output range.
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;
}

FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; }
};

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<
detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
.map(std::declval<T&>()))>::value>;

/**
\rst
An array of references to arguments. It can be implicitly converted into
`~fmt::basic_format_args` for passing into type-erased formatting functions
such as `~fmt::vformat`.
\endrst
*/
template <typename Context, typename... Args>
class format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
static const size_t num_args = sizeof...(Args);
static constexpr size_t num_named_args = detail::count_named_args<Args...>();
static const bool is_packed = num_args <= detail::max_packed_args;

using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>;

detail::arg_data<value_type, typename Context::char_type, num_args,
num_named_args>
data_;

friend class basic_format_args<Context>;

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);

public:
template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{detail::make_arg<is_packed, Context>(args)...} {
if (detail::const_check(num_named_args != 0))
detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};

/**
\rst
Constructs a `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::format_context`.
See `~fmt::arg` for lifetime considerations.
\endrst
*/
// Arguments are taken by lvalue references to avoid some lifetime issues.
template <typename Context = format_context, typename... T>
constexpr auto make_format_args(T&... args)
-> format_arg_store<Context, remove_const_t<T>...> {
return {args...};
}

/**
\rst
Returns a named argument to be used in a formatting function.
It should only be used in a call to a formatting function or
`dynamic_format_arg_store::push_back`.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
\endrst
*/
template <typename Char, typename T>
inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
static_assert(!detail::is_named_arg<T>(), "nested named arguments");
return {name, arg};
}
FMT_END_EXPORT

/**
\rst
A view of a collection of formatting arguments. To avoid lifetime issues it
Expand Down Expand Up @@ -1908,7 +1768,7 @@ template <typename Context> class basic_format_args {
constexpr auto is_packed() const -> bool {
return (desc_ & detail::is_unpacked_bit) == 0;
}
auto has_named_args() const -> bool {
constexpr auto has_named_args() const -> bool {
return (desc_ & detail::has_named_args_bit) != 0;
}

Expand Down Expand Up @@ -1978,7 +1838,7 @@ template <typename Context> class basic_format_args {
}

template <typename Char>
auto get_id(basic_string_view<Char> name) const -> int {
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;
Expand All @@ -1995,6 +1855,151 @@ template <typename Context> class basic_format_args {
}
};

// A formatting context.
class context {
private:
appender out_;
basic_format_args<context> args_;
detail::locale_ref loc_;

public:
/** The character type for the output. */
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>;

/**
Constructs a ``basic_format_context`` object. References to the arguments
are stored in the object so make sure they have appropriate lifetimes.
*/
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;

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_; }

// This function is intentionally not constexpr to give a compile-time error.
void on_error(const char* message) { throw_format_error(message); }

// Returns an iterator to the beginning of the output range.
FMT_CONSTEXPR auto out() -> iterator { return out_; }

// Advances the begin iterator to ``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>;

template <typename T, typename Char = char>
using is_formattable = bool_constant<!std::is_base_of<
detail::unformattable, decltype(detail::arg_mapper<buffer_context<Char>>()
.map(std::declval<T&>()))>::value>;

/**
\rst
An array of references to arguments. It can be implicitly converted into
`~fmt::basic_format_args` for passing into type-erased formatting functions
such as `~fmt::vformat`.
\endrst
*/
template <typename Context, typename... Args>
class format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
static const size_t num_args = sizeof...(Args);
static constexpr size_t num_named_args = detail::count_named_args<Args...>();
static const bool is_packed = num_args <= detail::max_packed_args;

using value_type = conditional_t<is_packed, detail::value<Context>,
basic_format_arg<Context>>;

detail::arg_data<value_type, typename Context::char_type, num_args,
num_named_args>
data_;

friend class basic_format_args<Context>;

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);

public:
template <typename... T>
FMT_CONSTEXPR FMT_INLINE format_arg_store(T&... args)
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{detail::make_arg<is_packed, Context>(args)...} {
if (detail::const_check(num_named_args != 0))
detail::init_named_args(data_.named_args(), 0, 0, args...);
}
};

/**
\rst
Constructs a `~fmt::format_arg_store` object that contains references to
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
can be omitted in which case it defaults to `~fmt::format_context`.
See `~fmt::arg` for lifetime considerations.
\endrst
*/
// Arguments are taken by lvalue references to avoid some lifetime issues.
template <typename Context = format_context, typename... T>
constexpr auto make_format_args(T&... args)
-> format_arg_store<Context, remove_const_t<T>...> {
return {args...};
}

/**
\rst
Returns a named argument to be used in a formatting function.
It should only be used in a call to a formatting function or
`dynamic_format_arg_store::push_back`.
**Example**::
fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23));
\endrst
*/
template <typename Char, typename T>
inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
static_assert(!detail::is_named_arg<T>(), "nested named arguments");
return {name, arg};
}
FMT_END_EXPORT

/** 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).
Expand Down
44 changes: 44 additions & 0 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
Loading

0 comments on commit a5ae9ae

Please sign in to comment.