Decouple format and parse contexts

This commit is contained in:
Victor Zverovich 2019-02-09 19:34:42 -08:00
parent 744e66bb08
commit 442fa1bd46
9 changed files with 166 additions and 183 deletions

View File

@ -481,10 +481,10 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
typedef output_range<decltype(ctx.out()), Char> range; typedef output_range<decltype(ctx.out()), Char> range;
basic_writer<range> w(range(ctx.out())); basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(spec.width_, internal::handle_dynamic_spec<internal::width_checker>(
width_ref, ctx); spec.width_, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx); precision, precision_ref, ctx, format_str.begin());
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision); out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out); internal::format_chrono_duration_unit<Period>(out);

View File

@ -687,7 +687,7 @@ template <typename Context> class value {
}; };
FMT_CONSTEXPR value(int val = 0) : int_value(val) {} FMT_CONSTEXPR value(int val = 0) : int_value(val) {}
value(unsigned val) { uint_value = val; } FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) { long_long_value = val; } value(long long val) { long_long_value = val; }
value(unsigned long long val) { ulong_long_value = val; } value(unsigned long long val) { ulong_long_value = val; }
value(double val) { double_value = val; } value(double val) { double_value = val; }
@ -936,7 +936,7 @@ template <typename Context> class basic_format_arg {
FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {} FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {}
FMT_EXPLICIT operator bool() const FMT_NOEXCEPT { FMT_CONSTEXPR FMT_EXPLICIT operator bool() const FMT_NOEXCEPT {
return type_ != internal::none_type; return type_ != internal::none_type;
} }
@ -1045,76 +1045,13 @@ class locale_ref {
public: public:
locale_ref() : locale_(FMT_NULL) {} locale_ref() : locale_(FMT_NULL) {}
template <typename Locale> explicit locale_ref(const Locale& loc); template <typename Locale> explicit locale_ref(const Locale& loc);
template <typename Locale> Locale get() const; template <typename Locale> Locale get() const;
}; };
template <typename OutputIt, typename Context, typename Char> template <typename OutputIt, typename Context, typename Char>
class context_base { class context_base {};
public:
typedef OutputIt iterator;
private:
basic_parse_context<Char> parse_context_;
iterator out_;
basic_format_args<Context> args_;
locale_ref loc_;
protected:
typedef Char char_type;
typedef basic_format_arg<Context> format_arg;
context_base(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<Context> ctx_args,
locale_ref loc = locale_ref())
: parse_context_(format_str), out_(out), args_(ctx_args), loc_(loc) {}
// Returns the argument with specified index.
format_arg do_get_arg(unsigned arg_id) {
format_arg arg = args_.get(arg_id);
if (!arg) parse_context_.on_error("argument index out of range");
return arg;
}
friend basic_parse_context<Char>& get_parse_context(context_base& ctx) {
return ctx.parse_context_;
}
// Checks if manual indexing is used and returns the argument with
// specified index.
format_arg arg(unsigned arg_id) {
return parse_context_.check_arg_id(arg_id) ? this->do_get_arg(arg_id)
: format_arg();
}
public:
FMT_DEPRECATED basic_parse_context<Char>& parse_context() {
return parse_context_;
}
// basic_format_context::arg() depends on this
// Cannot be marked as deprecated without causing warnings
/*FMT_DEPRECATED*/ basic_format_args<Context> args() const { return args_; }
basic_format_arg<Context> arg(unsigned id) const { return args_.get(id); }
internal::error_handler error_handler() {
return parse_context_.error_handler();
}
void on_error(const char* message) { parse_context_.on_error(message); }
// Returns an iterator to the beginning of the output range.
iterator out() { return out_; }
FMT_DEPRECATED iterator begin() { return out_; }
// Advances the begin iterator to ``it``.
void advance_to(iterator it) { out_ = it; }
locale_ref locale() { return loc_; }
};
template <typename Context, typename T> struct get_type { template <typename Context, typename T> struct get_type {
typedef decltype( typedef decltype(
@ -1153,54 +1090,54 @@ make_arg(const T& value) {
} // namespace internal } // namespace internal
// Formatting context. // Formatting context.
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char> class basic_format_context {
class basic_format_context
: public internal::context_base<
OutputIt, basic_format_context<OutputIt, Char>, Char> {
public: public:
/** The character type for the output. */ /** The character type for the output. */
typedef Char char_type; typedef Char char_type;
private:
OutputIt out_;
basic_format_args<basic_format_context> args_;
internal::arg_map<basic_format_context> map_;
internal::locale_ref loc_;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
public:
typedef OutputIt iterator;
typedef basic_format_arg<basic_format_context> format_arg;
// using formatter_type = formatter<T, char_type>; // using formatter_type = formatter<T, char_type>;
template <typename T> struct formatter_type { template <typename T> struct formatter_type {
typedef formatter<T, char_type> type; typedef formatter<T, char_type> type;
}; };
private:
internal::arg_map<basic_format_context> map_;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
typedef internal::context_base<OutputIt, basic_format_context, Char> base;
using base::arg;
public:
using typename base::iterator;
typedef typename base::format_arg format_arg;
/** /**
Constructs a ``basic_format_context`` object. References to the arguments are Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes. stored in the object so make sure they have appropriate lifetimes.
*/ */
basic_format_context(OutputIt out, basic_string_view<char_type> format_str, basic_format_context(OutputIt out,
basic_format_args<basic_format_context> ctx_args, basic_format_args<basic_format_context> ctx_args,
internal::locale_ref loc = internal::locale_ref()) internal::locale_ref loc = internal::locale_ref())
: base(out, format_str, ctx_args, loc) {} : out_(out), args_(ctx_args), loc_(loc) {}
format_arg next_arg() { format_arg arg(unsigned id) const { return args_.get(id); }
return this->do_get_arg(get_parse_context(*this).next_arg_id());
}
format_arg arg(unsigned arg_id) { return this->do_get_arg(arg_id); }
// Checks if manual indexing is used and returns the argument with the // Checks if manual indexing is used and returns the argument with the
// specified name. // specified name.
format_arg arg(basic_string_view<char_type> name); format_arg arg(basic_string_view<char_type> name);
FMT_DEPRECATED format_arg get_arg(unsigned arg_id) { return arg(arg_id); } internal::error_handler error_handler() { return {}; }
FMT_DEPRECATED format_arg get_arg(basic_string_view<char_type> name) { void on_error(const char* message) { error_handler().on_error(message); }
return arg(name);
} // Returns an iterator to the beginning of the output range.
iterator out() { return out_; }
// Advances the begin iterator to ``it``.
void advance_to(iterator it) { out_ = it; }
internal::locale_ref locale() { return loc_; }
}; };
template <typename Char> struct buffer_context { template <typename Char> struct buffer_context {
@ -1399,6 +1336,13 @@ struct char_t : std::enable_if<internal::is_string<S>::value,
#endif #endif
namespace internal { namespace internal {
template <typename Context>
FMT_CONSTEXPR basic_format_arg<Context> get_arg(Context& ctx, unsigned id) {
auto arg = ctx.arg(id);
if (!arg) ctx.on_error("argument index out of range");
return arg;
}
template <typename Char> struct named_arg_base { template <typename Char> struct named_arg_base {
basic_string_view<Char> name; basic_string_view<Char> name;

View File

@ -1567,16 +1567,20 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(const Char*& begin,
return value; return value;
} }
template <typename Char, typename Context> template <typename Context> class custom_formatter : public function<bool> {
class custom_formatter : public function<bool> {
private: private:
typedef typename Context::char_type char_type;
basic_parse_context<char_type>& parse_ctx_;
Context& ctx_; Context& ctx_;
public: public:
explicit custom_formatter(Context& ctx) : ctx_(ctx) {} explicit custom_formatter(basic_parse_context<char_type>& parse_ctx,
Context& ctx)
: parse_ctx_(parse_ctx), ctx_(ctx) {}
bool operator()(typename basic_format_arg<Context>::handle h) const { bool operator()(typename basic_format_arg<Context>::handle h) const {
h.format(get_parse_context(ctx_), ctx_); h.format(parse_ctx_, ctx_);
return true; return true;
} }
@ -1769,7 +1773,9 @@ class specs_handler : public specs_setter<typename Context::char_type> {
FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs, FMT_CONSTEXPR specs_handler(basic_format_specs<char_type>& specs,
ParseContext& parse_ctx, Context& ctx) ParseContext& parse_ctx, Context& ctx)
: specs_setter<char_type>(specs), parse_ctx_(parse_ctx), context_(ctx) {} : specs_setter<char_type>(specs),
parse_context_(parse_ctx),
context_(ctx) {}
template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id), set_dynamic_spec<width_checker>(this->specs_.width_, get_arg(arg_id),
@ -1787,14 +1793,21 @@ class specs_handler : public specs_setter<typename Context::char_type> {
// This is only needed for compatibility with gcc 4.4. // This is only needed for compatibility with gcc 4.4.
typedef typename Context::format_arg format_arg; typedef typename Context::format_arg format_arg;
FMT_CONSTEXPR format_arg get_arg(auto_id) { return context_.next_arg(); } FMT_CONSTEXPR format_arg get_arg(auto_id) {
return internal::get_arg(context_, parse_context_.next_arg_id());
}
template <typename Id> FMT_CONSTEXPR format_arg get_arg(Id arg_id) { FMT_CONSTEXPR format_arg get_arg(unsigned arg_id) {
parse_ctx_.check_arg_id(arg_id); parse_context_.check_arg_id(arg_id);
return internal::get_arg(context_, arg_id);
}
FMT_CONSTEXPR format_arg get_arg(basic_string_view<char_type> arg_id) {
parse_context_.check_arg_id(arg_id);
return context_.arg(arg_id); return context_.arg(arg_id);
} }
ParseContext& parse_ctx_; ParseContext& parse_context_;
Context& context_; Context& context_;
}; };
@ -2264,7 +2277,8 @@ struct format_type
template <template <typename> class Handler, typename Spec, typename Context> template <template <typename> class Handler, typename Spec, typename Context>
void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref, void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
Context& ctx) { Context& ctx,
const typename Context::char_type* format_str) {
typedef typename Context::char_type char_type; typedef typename Context::char_type char_type;
switch (ref.kind) { switch (ref.kind) {
case arg_ref<char_type>::NONE: case arg_ref<char_type>::NONE:
@ -2274,7 +2288,7 @@ void handle_dynamic_spec(Spec& value, arg_ref<typename Context::char_type> ref,
ctx.error_handler()); ctx.error_handler());
break; break;
case arg_ref<char_type>::NAME: { case arg_ref<char_type>::NAME: {
const auto arg_id = ref.val.name.to_view(get_parse_context(ctx).begin()); const auto arg_id = ref.val.name.to_view(format_str);
internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id), internal::set_dynamic_spec<Handler>(value, ctx.arg(arg_id),
ctx.error_handler()); ctx.error_handler());
} break; } break;
@ -2294,6 +2308,7 @@ class arg_formatter
typedef basic_format_context<typename base::iterator, char_type> context_type; typedef basic_format_context<typename base::iterator, char_type> context_type;
context_type& ctx_; context_type& ctx_;
basic_parse_context<char_type>* parse_ctx_;
public: public:
typedef Range range; typedef Range range;
@ -2307,8 +2322,12 @@ class arg_formatter
*spec* contains format specifier information for standard argument types. *spec* contains format specifier information for standard argument types.
\endrst \endrst
*/ */
explicit arg_formatter(context_type& ctx, format_specs* spec = FMT_NULL) explicit arg_formatter(context_type& ctx,
: base(Range(ctx.out()), spec, ctx.locale()), ctx_(ctx) {} basic_parse_context<char_type>* parse_ctx = FMT_NULL,
format_specs* spec = FMT_NULL)
: base(Range(ctx.out()), spec, ctx.locale()),
ctx_(ctx),
parse_ctx_(parse_ctx) {}
FMT_DEPRECATED arg_formatter(context_type& ctx, format_specs& spec) FMT_DEPRECATED arg_formatter(context_type& ctx, format_specs& spec)
: base(Range(ctx.out()), &spec), ctx_(ctx) {} : base(Range(ctx.out()), &spec), ctx_(ctx) {}
@ -2317,7 +2336,7 @@ class arg_formatter
/** Formats an argument of a user-defined type. */ /** Formats an argument of a user-defined type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) { iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(get_parse_context(ctx_), ctx_); handle.format(*parse_ctx_, ctx_);
return this->out(); return this->out();
} }
}; };
@ -3023,10 +3042,13 @@ template <typename T, typename Char>
struct formatter<T, Char, struct formatter<T, Char,
typename std::enable_if<internal::format_type< typename std::enable_if<internal::format_type<
typename buffer_context<Char>::type, T>::value>::type> { typename buffer_context<Char>::type, T>::value>::type> {
FMT_CONSTEXPR formatter() : format_str_(FMT_NULL) {}
// Parses format specifiers stopping either at the end of the range or at the // Parses format specifiers stopping either at the end of the range or at the
// terminating '}'. // terminating '}'.
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) { FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
format_str_ = ctx.begin();
typedef internal::dynamic_specs_handler<ParseContext> handler_type; typedef internal::dynamic_specs_handler<ParseContext> handler_type;
auto type = auto type =
internal::get_type<typename buffer_context<Char>::type, T>::value; internal::get_type<typename buffer_context<Char>::type, T>::value;
@ -3078,18 +3100,19 @@ struct formatter<T, Char,
template <typename FormatContext> template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(
specs_.width_, specs_.width_ref, ctx); specs_.width_, specs_.width_ref, ctx, format_str_);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx); specs_.precision, specs_.precision_ref, ctx, format_str_);
typedef output_range<typename FormatContext::iterator, typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_type> typename FormatContext::char_type>
range_type; range_type;
return visit_format_arg(arg_formatter<range_type>(ctx, &specs_), return visit_format_arg(arg_formatter<range_type>(ctx, FMT_NULL, &specs_),
internal::make_arg<FormatContext>(val)); internal::make_arg<FormatContext>(val));
} }
private: private:
internal::dynamic_format_specs<Char> specs_; internal::dynamic_format_specs<Char> specs_;
const Char* format_str_;
}; };
// A formatter for types known only at run time such as variant alternatives. // A formatter for types known only at run time such as variant alternatives.
@ -3115,6 +3138,7 @@ template <typename Char = char> class dynamic_formatter {
public: public:
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin();
// Checks are deferred to formatting time when the argument type is known. // Checks are deferred to formatting time when the argument type is known.
internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx); internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
return parse_format_specs(ctx.begin(), ctx.end(), handler); return parse_format_specs(ctx.begin(), ctx.end(), handler);
@ -3138,7 +3162,7 @@ template <typename Char = char> class dynamic_formatter {
typedef output_range<typename FormatContext::iterator, typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_type> typename FormatContext::char_type>
range; range;
visit_format_arg(arg_formatter<range>(ctx, &specs_), visit_format_arg(arg_formatter<range>(ctx, FMT_NULL, &specs_),
internal::make_arg<FormatContext>(val)); internal::make_arg<FormatContext>(val));
return ctx.out(); return ctx.out();
} }
@ -3146,18 +3170,19 @@ template <typename Char = char> class dynamic_formatter {
private: private:
template <typename Context> void handle_specs(Context& ctx) { template <typename Context> void handle_specs(Context& ctx) {
internal::handle_dynamic_spec<internal::width_checker>( internal::handle_dynamic_spec<internal::width_checker>(
specs_.width_, specs_.width_ref, ctx); specs_.width_, specs_.width_ref, ctx, format_str_);
internal::handle_dynamic_spec<internal::precision_checker>( internal::handle_dynamic_spec<internal::precision_checker>(
specs_.precision, specs_.precision_ref, ctx); specs_.precision, specs_.precision_ref, ctx, format_str_);
} }
internal::dynamic_format_specs<Char> specs_; internal::dynamic_format_specs<Char> specs_;
const Char* format_str_;
}; };
template <typename Range, typename Char> template <typename Range, typename Char>
typename basic_format_context<Range, Char>::format_arg typename basic_format_context<Range, Char>::format_arg
basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) { basic_format_context<Range, Char>::arg(basic_string_view<char_type> name) {
map_.init(this->args()); map_.init(args_);
format_arg arg = map_.find(name); format_arg arg = map_.find(name);
if (arg.type() == internal::none_type) this->on_error("argument not found"); if (arg.type() == internal::none_type) this->on_error("argument not found");
return arg; return arg;
@ -3170,7 +3195,7 @@ struct format_handler : internal::error_handler {
format_handler(range r, basic_string_view<Char> str, format_handler(range r, basic_string_view<Char> str,
basic_format_args<Context> format_args, basic_format_args<Context> format_args,
internal::locale_ref loc) internal::locale_ref loc)
: context(r.begin(), str, format_args, loc) {} : parse_context(str), context(r.begin(), format_args, loc) {}
void on_text(const Char* begin, const Char* end) { void on_text(const Char* begin, const Char* end) {
auto size = internal::to_unsigned(end - begin); auto size = internal::to_unsigned(end - begin);
@ -3180,39 +3205,42 @@ struct format_handler : internal::error_handler {
context.advance_to(out); context.advance_to(out);
} }
void on_arg_id() { arg = context.next_arg(); } void get_arg(unsigned id) { arg = internal::get_arg(context, id); }
void on_arg_id() { get_arg(parse_context.next_arg_id()); }
void on_arg_id(unsigned id) { void on_arg_id(unsigned id) {
get_parse_context(context).check_arg_id(id); parse_context.check_arg_id(id);
arg = context.arg(id); get_arg(id);
} }
void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); } void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); }
void on_replacement_field(const Char* p) { void on_replacement_field(const Char* p) {
get_parse_context(context).advance_to(p); parse_context.advance_to(p);
internal::custom_formatter<Char, Context> f(context); internal::custom_formatter<Context> f(parse_context, context);
if (!visit_format_arg(f, arg)) if (!visit_format_arg(f, arg))
context.advance_to(visit_format_arg(ArgFormatter(context), arg)); context.advance_to(
visit_format_arg(ArgFormatter(context, &parse_context), arg));
} }
const Char* on_format_specs(const Char* begin, const Char* end) { const Char* on_format_specs(const Char* begin, const Char* end) {
auto& parse_ctx = get_parse_context(context); parse_context.advance_to(begin);
parse_ctx.advance_to(begin); internal::custom_formatter<Context> f(parse_context, context);
internal::custom_formatter<Char, Context> f(context); if (visit_format_arg(f, arg)) return parse_context.begin();
if (visit_format_arg(f, arg)) return parse_ctx.begin();
basic_format_specs<Char> specs; basic_format_specs<Char> specs;
using internal::specs_handler; using internal::specs_handler;
typedef basic_parse_context<Char> parse_context; typedef basic_parse_context<Char> parse_context_t;
internal::specs_checker<specs_handler<parse_context, Context>> handler( internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
specs_handler<parse_context, Context>(specs, get_parse_context(context), specs_handler<parse_context_t, Context>(specs, parse_context, context),
context),
arg.type()); arg.type());
begin = parse_format_specs(begin, end, handler); begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string"); if (begin == end || *begin != '}') on_error("missing '}' in format string");
parse_ctx.advance_to(begin); parse_context.advance_to(begin);
context.advance_to(visit_format_arg(ArgFormatter(context, &specs), arg)); context.advance_to(
visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
return begin; return begin;
} }
basic_parse_context<Char> parse_context;
Context context; Context context;
basic_format_arg<Context> arg; basic_format_arg<Context> arg;
}; };

View File

@ -253,7 +253,8 @@ class prepared_format {
typename Context::iterator vformat_to(Range out, typename Context::iterator vformat_to(Range out,
basic_format_args<Context> args) const { basic_format_args<Context> args) const {
const auto format_view = internal::to_string_view(format_); const auto format_view = internal::to_string_view(format_);
Context ctx(out.begin(), format_view, args); basic_parse_context<char_type> parse_ctx(format_view);
Context ctx(out.begin(), args);
const auto& parts = parts_provider_.parts(); const auto& parts = parts_provider_.parts();
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) { for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
@ -270,14 +271,14 @@ class prepared_format {
} break; } break;
case format_part_t::which_value::argument_id: { case format_part_t::which_value::argument_id: {
advance_parse_context_to_specification(ctx, part); advance_parse_context_to_specification(parse_ctx, part);
format_arg<Range>(ctx, value.arg_id); format_arg<Range>(parse_ctx, ctx, value.arg_id);
} break; } break;
case format_part_t::which_value::named_argument_id: { case format_part_t::which_value::named_argument_id: {
advance_parse_context_to_specification(ctx, part); advance_parse_context_to_specification(parse_ctx, part);
const auto named_arg_id = value.named_arg_id.to_view(format_view); const auto named_arg_id = value.named_arg_id.to_view(format_view);
format_arg<Range>(ctx, named_arg_id); format_arg<Range>(parse_ctx, ctx, named_arg_id);
} break; } break;
case format_part_t::which_value::specification: { case format_part_t::which_value::specification: {
const auto& arg_id_value = value.spec.arg_id.val; const auto& arg_id_value = value.spec.arg_id.val;
@ -289,15 +290,15 @@ class prepared_format {
auto specs = value.spec.parsed_specs; auto specs = value.spec.parsed_specs;
handle_dynamic_spec<internal::width_checker>(specs.width_, handle_dynamic_spec<internal::width_checker>(
specs.width_ref, ctx); specs.width_, specs.width_ref, ctx, format_view.begin());
handle_dynamic_spec<internal::precision_checker>( handle_dynamic_spec<internal::precision_checker>(
specs.precision, specs.precision_ref, ctx); specs.precision, specs.precision_ref, ctx, format_view.begin());
check_prepared_specs(specs, arg.type()); check_prepared_specs(specs, arg.type());
advance_parse_context_to_specification(ctx, part); advance_parse_context_to_specification(parse_ctx, part);
ctx.advance_to( ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &specs), arg)); visit_format_arg(arg_formatter<Range>(ctx, FMT_NULL, &specs), arg));
} break; } break;
} }
} }
@ -305,17 +306,18 @@ class prepared_format {
return ctx.out(); return ctx.out();
} }
template <typename Context> void advance_parse_context_to_specification(
void advance_parse_context_to_specification(Context& ctx, basic_parse_context<char_type>& parse_ctx,
const format_part_t& part) const { const format_part_t& part) const {
const auto view = to_string_view(format_); const auto view = to_string_view(format_);
const auto specification_begin = view.data() + part.end_of_argument_id; const auto specification_begin = view.data() + part.end_of_argument_id;
get_parse_context(ctx).advance_to(specification_begin); parse_ctx.advance_to(specification_begin);
} }
template <typename Range, typename Context, typename Id> template <typename Range, typename Context, typename Id>
void format_arg(Context& ctx, Id arg_id) const { void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
get_parse_context(ctx).check_arg_id(arg_id); Id arg_id) const {
parse_ctx.check_arg_id(arg_id);
const auto stopped_at = const auto stopped_at =
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id)); visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
ctx.advance_to(stopped_at); ctx.advance_to(stopped_at);

View File

@ -250,7 +250,7 @@ class printf_arg_formatter
\endrst \endrst
*/ */
printf_arg_formatter(iterator iter, format_specs& spec, context_type& ctx) printf_arg_formatter(iterator iter, format_specs& spec, context_type& ctx)
: base(Range(iter), &spec, ctx.locale()), context_(ctx) {} : base(Range(iter), &spec, internal::locale_ref()), context_(ctx) {}
template <typename T> template <typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type typename std::enable_if<std::is_integral<T>::value, iterator>::type
@ -319,7 +319,7 @@ class printf_arg_formatter
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) { iterator operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(get_parse_context(context_), context_); handle.format(context_.parse_context(), context_);
return this->out(); return this->out();
} }
}; };
@ -339,11 +339,7 @@ template <typename T> struct printf_formatter {
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char, typename ArgFormatter> template <typename OutputIt, typename Char, typename ArgFormatter>
class basic_printf_context : class basic_printf_context {
// Inherit publicly as a workaround for the icc bug
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
public internal::context_base<
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
public: public:
/** The character type for the output. */ /** The character type for the output. */
typedef Char char_type; typedef Char char_type;
@ -354,9 +350,13 @@ class basic_printf_context :
private: private:
typedef internal::context_base<OutputIt, basic_printf_context, Char> base; typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
typedef typename base::format_arg format_arg; typedef basic_format_arg<basic_printf_context> format_arg;
typedef basic_format_specs<char_type> format_specs; typedef basic_format_specs<char_type> format_specs;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& spec, const Char*& it, const Char* end); static void parse_flags(format_specs& spec, const Char*& it, const Char* end);
// Returns the argument with specified index or, if arg_index is equal // Returns the argument with specified index or, if arg_index is equal
@ -376,10 +376,18 @@ class basic_printf_context :
*/ */
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: base(out, format_str, args) {} : out_(out), args_(args), parse_ctx_(format_str) {}
using base::advance_to; OutputIt out() { return out_; }
using base::out; void advance_to(OutputIt it) { out_ = it; }
format_arg arg(unsigned id) const { return args_.get(id); }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */ /** Formats stored arguments and writes the output to the range. */
OutputIt format(); OutputIt format();
@ -416,8 +424,10 @@ template <typename OutputIt, typename Char, typename AF>
typename basic_printf_context<OutputIt, Char, AF>::format_arg typename basic_printf_context<OutputIt, Char, AF>::format_arg
basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) { basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max()) if (arg_index == std::numeric_limits<unsigned>::max())
return this->do_get_arg(get_parse_context(*this).next_arg_id()); arg_index = parse_ctx_.next_arg_id();
return base::arg(arg_index - 1); else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
} }
template <typename OutputIt, typename Char, typename AF> template <typename OutputIt, typename Char, typename AF>
@ -461,9 +471,8 @@ unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
template <typename OutputIt, typename Char, typename AF> template <typename OutputIt, typename Char, typename AF>
OutputIt basic_printf_context<OutputIt, Char, AF>::format() { OutputIt basic_printf_context<OutputIt, Char, AF>::format() {
auto out = this->out(); auto out = this->out();
const auto range = get_parse_context(*this); const Char* start = parse_ctx_.begin();
const Char* const end = range.end(); const Char* end = parse_ctx_.end();
const Char* start = range.begin();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
char_type c = *it++; char_type c = *it++;

View File

@ -377,8 +377,9 @@ struct check_custom {
void grow(std::size_t) {} void grow(std::size_t) {}
} buffer; } buffer;
fmt::internal::basic_buffer<char>& base = buffer; fmt::internal::basic_buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); fmt::format_parse_context parse_ctx("");
h.format(get_parse_context(ctx), ctx); fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
h.format(parse_ctx, ctx);
EXPECT_EQ("test", std::string(buffer.data, buffer.size())); EXPECT_EQ("test", std::string(buffer.data, buffer.size()));
return test_result(); return test_result();
} }

View File

@ -20,8 +20,9 @@ class custom_arg_formatter
typedef fmt::arg_formatter<range> base; typedef fmt::arg_formatter<range> base;
custom_arg_formatter(fmt::format_context& ctx, custom_arg_formatter(fmt::format_context& ctx,
fmt::format_parse_context* parse_ctx,
fmt::format_specs* s = FMT_NULL) fmt::format_specs* s = FMT_NULL)
: base(ctx, s) {} : base(ctx, parse_ctx, s) {}
using base::operator(); using base::operator();

View File

@ -1927,7 +1927,8 @@ class mock_arg_formatter
typedef fmt::internal::arg_formatter_base<buffer_range> base; typedef fmt::internal::arg_formatter_base<buffer_range> base;
typedef buffer_range range; typedef buffer_range range;
mock_arg_formatter(fmt::format_context& ctx, fmt::format_specs* s = FMT_NULL) mock_arg_formatter(fmt::format_context& ctx, fmt::format_parse_context*,
fmt::format_specs* s = FMT_NULL)
: base(fmt::internal::get_container(ctx.out()), s, ctx.locale()) { : base(fmt::internal::get_container(ctx.out()), s, ctx.locale()) {
EXPECT_CALL(*this, call(42)); EXPECT_CALL(*this, call(42));
} }
@ -2167,7 +2168,7 @@ TEST(FormatTest, ConstexprParseFormatSpecs) {
struct test_parse_context { struct test_parse_context {
typedef char char_type; typedef char char_type;
FMT_CONSTEXPR unsigned next_arg_id() { return 33; } FMT_CONSTEXPR unsigned next_arg_id() { return 11; }
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {} template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
FMT_CONSTEXPR const char* begin() { return FMT_NULL; } FMT_CONSTEXPR const char* begin() { return FMT_NULL; }
@ -2184,13 +2185,9 @@ struct test_context {
typedef fmt::formatter<T, char_type> type; typedef fmt::formatter<T, char_type> type;
}; };
FMT_CONSTEXPR fmt::basic_format_arg<test_context> next_arg() {
return fmt::internal::make_arg<test_context>(11);
}
template <typename Id> template <typename Id>
FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id) { FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id id) {
return fmt::internal::make_arg<test_context>(22); return fmt::internal::make_arg<test_context>(id);
} }
void on_error(const char*) {} void on_error(const char*) {}
@ -2219,10 +2216,10 @@ TEST(FormatTest, ConstexprSpecsHandler) {
static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_specs("0").align() == fmt::ALIGN_NUMERIC, "");
static_assert(parse_specs("42").width() == 42, ""); static_assert(parse_specs("42").width() == 42, "");
static_assert(parse_specs("{}").width() == 11, ""); static_assert(parse_specs("{}").width() == 11, "");
static_assert(parse_specs("{0}").width() == 22, ""); static_assert(parse_specs("{22}").width() == 22, "");
static_assert(parse_specs(".42").precision == 42, ""); static_assert(parse_specs(".42").precision == 42, "");
static_assert(parse_specs(".{}").precision == 11, ""); static_assert(parse_specs(".{}").precision == 11, "");
static_assert(parse_specs(".{0}").precision == 22, ""); static_assert(parse_specs(".{22}").precision == 22, "");
static_assert(parse_specs("d").type == 'd', ""); static_assert(parse_specs("d").type == 'd', "");
} }
@ -2245,10 +2242,10 @@ TEST(FormatTest, ConstexprDynamicSpecsHandler) {
static_assert(parse_dynamic_specs("#").has(fmt::HASH_FLAG), ""); static_assert(parse_dynamic_specs("#").has(fmt::HASH_FLAG), "");
static_assert(parse_dynamic_specs("0").align() == fmt::ALIGN_NUMERIC, ""); static_assert(parse_dynamic_specs("0").align() == fmt::ALIGN_NUMERIC, "");
static_assert(parse_dynamic_specs("42").width() == 42, ""); static_assert(parse_dynamic_specs("42").width() == 42, "");
static_assert(parse_dynamic_specs("{}").width_ref.val.index == 33, ""); static_assert(parse_dynamic_specs("{}").width_ref.val.index == 11, "");
static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, ""); static_assert(parse_dynamic_specs("{42}").width_ref.val.index == 42, "");
static_assert(parse_dynamic_specs(".42").precision == 42, ""); static_assert(parse_dynamic_specs(".42").precision == 42, "");
static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 33, ""); static_assert(parse_dynamic_specs(".{}").precision_ref.val.index == 11, "");
static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, ""); static_assert(parse_dynamic_specs(".{42}").precision_ref.val.index == 42, "");
static_assert(parse_dynamic_specs("d").type == 'd', ""); static_assert(parse_dynamic_specs("d").type == 'd', "");
} }

View File

@ -54,14 +54,15 @@ TEST(OStreamTest, Enum) {
typedef fmt::back_insert_range<fmt::internal::buffer> range; typedef fmt::back_insert_range<fmt::internal::buffer> range;
struct test_arg_formatter : fmt::arg_formatter<range> { struct test_arg_formatter : fmt::arg_formatter<range> {
fmt::format_parse_context parse_ctx;
test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s) test_arg_formatter(fmt::format_context& ctx, fmt::format_specs& s)
: fmt::arg_formatter<range>(ctx, &s) {} : fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {}
}; };
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::internal::buffer& base = buffer; fmt::internal::buffer& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); fmt::format_context ctx(std::back_inserter(base), fmt::format_args());
fmt::format_specs spec; fmt::format_specs spec;
test_arg_formatter af(ctx, spec); test_arg_formatter af(ctx, spec);
fmt::visit_format_arg( fmt::visit_format_arg(