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);
typedef output_range<decltype(ctx.out()), Char> range;
basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(spec.width_,
width_ref, ctx);
internal::handle_dynamic_spec<internal::width_checker>(
spec.width_, width_ref, ctx, format_str.begin());
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
precision, precision_ref, ctx, format_str.begin());
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
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) {}
value(unsigned val) { uint_value = val; }
FMT_CONSTEXPR value(unsigned val) : uint_value(val) {}
value(long long val) { long_long_value = val; }
value(unsigned long long val) { ulong_long_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_EXPLICIT operator bool() const FMT_NOEXCEPT {
FMT_CONSTEXPR FMT_EXPLICIT operator bool() const FMT_NOEXCEPT {
return type_ != internal::none_type;
}
@ -1045,76 +1045,13 @@ class locale_ref {
public:
locale_ref() : locale_(FMT_NULL) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
template <typename Locale> Locale get() const;
};
template <typename OutputIt, typename Context, typename Char>
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_; }
};
class context_base {};
template <typename Context, typename T> struct get_type {
typedef decltype(
@ -1153,54 +1090,54 @@ make_arg(const T& value) {
} // namespace internal
// Formatting context.
template <typename OutputIt, typename Char>
class basic_format_context
: public internal::context_base<
OutputIt, basic_format_context<OutputIt, Char>, Char> {
template <typename OutputIt, typename Char> class basic_format_context {
public:
/** The character type for the output. */
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>;
template <typename T> struct formatter_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
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,
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() {
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); }
format_arg arg(unsigned id) const { return args_.get(id); }
// Checks if manual indexing is used and returns the argument with the
// specified name.
format_arg arg(basic_string_view<char_type> name);
FMT_DEPRECATED format_arg get_arg(unsigned arg_id) { return arg(arg_id); }
FMT_DEPRECATED format_arg get_arg(basic_string_view<char_type> name) {
return arg(name);
}
internal::error_handler error_handler() { return {}; }
void on_error(const char* message) { error_handler().on_error(message); }
// 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 {
@ -1399,6 +1336,13 @@ struct char_t : std::enable_if<internal::is_string<S>::value,
#endif
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 {
basic_string_view<Char> name;

View File

@ -1567,16 +1567,20 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(const Char*& begin,
return value;
}
template <typename Char, typename Context>
class custom_formatter : public function<bool> {
template <typename Context> class custom_formatter : public function<bool> {
private:
typedef typename Context::char_type char_type;
basic_parse_context<char_type>& parse_ctx_;
Context& ctx_;
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 {
h.format(get_parse_context(ctx_), ctx_);
h.format(parse_ctx_, ctx_);
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,
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) {
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.
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) {
parse_ctx_.check_arg_id(arg_id);
FMT_CONSTEXPR format_arg get_arg(unsigned 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);
}
ParseContext& parse_ctx_;
ParseContext& parse_context_;
Context& context_;
};
@ -2264,7 +2277,8 @@ struct format_type
template <template <typename> class Handler, typename Spec, typename Context>
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;
switch (ref.kind) {
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());
break;
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),
ctx.error_handler());
} break;
@ -2294,6 +2308,7 @@ class arg_formatter
typedef basic_format_context<typename base::iterator, char_type> context_type;
context_type& ctx_;
basic_parse_context<char_type>* parse_ctx_;
public:
typedef Range range;
@ -2307,8 +2322,12 @@ class arg_formatter
*spec* contains format specifier information for standard argument types.
\endrst
*/
explicit arg_formatter(context_type& ctx, format_specs* spec = FMT_NULL)
: base(Range(ctx.out()), spec, ctx.locale()), ctx_(ctx) {}
explicit arg_formatter(context_type& 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)
: base(Range(ctx.out()), &spec), ctx_(ctx) {}
@ -2317,7 +2336,7 @@ class arg_formatter
/** Formats an argument of a user-defined type. */
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();
}
};
@ -3023,10 +3042,13 @@ template <typename T, typename Char>
struct formatter<T, Char,
typename std::enable_if<internal::format_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
// terminating '}'.
template <typename ParseContext>
FMT_CONSTEXPR typename ParseContext::iterator parse(ParseContext& ctx) {
format_str_ = ctx.begin();
typedef internal::dynamic_specs_handler<ParseContext> handler_type;
auto type =
internal::get_type<typename buffer_context<Char>::type, T>::value;
@ -3078,18 +3100,19 @@ struct formatter<T, Char,
template <typename FormatContext>
auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()) {
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>(
specs_.precision, specs_.precision_ref, ctx);
specs_.precision, specs_.precision_ref, ctx, format_str_);
typedef output_range<typename FormatContext::iterator,
typename FormatContext::char_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));
}
private:
internal::dynamic_format_specs<Char> specs_;
const Char* format_str_;
};
// 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:
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
format_str_ = ctx.begin();
// Checks are deferred to formatting time when the argument type is known.
internal::dynamic_specs_handler<ParseContext> handler(specs_, ctx);
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,
typename FormatContext::char_type>
range;
visit_format_arg(arg_formatter<range>(ctx, &specs_),
visit_format_arg(arg_formatter<range>(ctx, FMT_NULL, &specs_),
internal::make_arg<FormatContext>(val));
return ctx.out();
}
@ -3146,18 +3170,19 @@ template <typename Char = char> class dynamic_formatter {
private:
template <typename Context> void handle_specs(Context& ctx) {
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>(
specs_.precision, specs_.precision_ref, ctx);
specs_.precision, specs_.precision_ref, ctx, format_str_);
}
internal::dynamic_format_specs<Char> specs_;
const Char* format_str_;
};
template <typename Range, typename Char>
typename basic_format_context<Range, Char>::format_arg
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);
if (arg.type() == internal::none_type) this->on_error("argument not found");
return arg;
@ -3170,7 +3195,7 @@ struct format_handler : internal::error_handler {
format_handler(range r, basic_string_view<Char> str,
basic_format_args<Context> format_args,
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) {
auto size = internal::to_unsigned(end - begin);
@ -3180,39 +3205,42 @@ struct format_handler : internal::error_handler {
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) {
get_parse_context(context).check_arg_id(id);
arg = context.arg(id);
parse_context.check_arg_id(id);
get_arg(id);
}
void on_arg_id(basic_string_view<Char> id) { arg = context.arg(id); }
void on_replacement_field(const Char* p) {
get_parse_context(context).advance_to(p);
internal::custom_formatter<Char, Context> f(context);
parse_context.advance_to(p);
internal::custom_formatter<Context> f(parse_context, context);
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) {
auto& parse_ctx = get_parse_context(context);
parse_ctx.advance_to(begin);
internal::custom_formatter<Char, Context> f(context);
if (visit_format_arg(f, arg)) return parse_ctx.begin();
parse_context.advance_to(begin);
internal::custom_formatter<Context> f(parse_context, context);
if (visit_format_arg(f, arg)) return parse_context.begin();
basic_format_specs<Char> specs;
using internal::specs_handler;
typedef basic_parse_context<Char> parse_context;
internal::specs_checker<specs_handler<parse_context, Context>> handler(
specs_handler<parse_context, Context>(specs, get_parse_context(context),
context),
typedef basic_parse_context<Char> parse_context_t;
internal::specs_checker<specs_handler<parse_context_t, Context>> handler(
specs_handler<parse_context_t, Context>(specs, parse_context, context),
arg.type());
begin = parse_format_specs(begin, end, handler);
if (begin == end || *begin != '}') on_error("missing '}' in format string");
parse_ctx.advance_to(begin);
context.advance_to(visit_format_arg(ArgFormatter(context, &specs), arg));
parse_context.advance_to(begin);
context.advance_to(
visit_format_arg(ArgFormatter(context, &parse_context, &specs), arg));
return begin;
}
basic_parse_context<Char> parse_context;
Context context;
basic_format_arg<Context> arg;
};

View File

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

View File

@ -250,7 +250,7 @@ class printf_arg_formatter
\endrst
*/
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>
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. */
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();
}
};
@ -339,11 +339,7 @@ template <typename T> struct printf_formatter {
/** This template formats data and writes the output to a writer. */
template <typename OutputIt, typename Char, typename ArgFormatter>
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> {
class basic_printf_context {
public:
/** The character type for the output. */
typedef Char char_type;
@ -354,9 +350,13 @@ class basic_printf_context :
private:
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;
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);
// 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_format_args<basic_printf_context> args)
: base(out, format_str, args) {}
: out_(out), args_(args), parse_ctx_(format_str) {}
using base::advance_to;
using base::out;
OutputIt out() { return 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. */
OutputIt format();
@ -416,8 +424,10 @@ template <typename OutputIt, typename Char, typename AF>
typename basic_printf_context<OutputIt, Char, AF>::format_arg
basic_printf_context<OutputIt, Char, AF>::get_arg(unsigned arg_index) {
if (arg_index == std::numeric_limits<unsigned>::max())
return this->do_get_arg(get_parse_context(*this).next_arg_id());
return base::arg(arg_index - 1);
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
}
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>
OutputIt basic_printf_context<OutputIt, Char, AF>::format() {
auto out = this->out();
const auto range = get_parse_context(*this);
const Char* const end = range.end();
const Char* start = range.begin();
const Char* start = parse_ctx_.begin();
const Char* end = parse_ctx_.end();
auto it = start;
while (it != end) {
char_type c = *it++;

View File

@ -377,8 +377,9 @@ struct check_custom {
void grow(std::size_t) {}
} buffer;
fmt::internal::basic_buffer<char>& base = buffer;
fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args());
h.format(get_parse_context(ctx), ctx);
fmt::format_parse_context parse_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()));
return test_result();
}

View File

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

View File

@ -1927,7 +1927,8 @@ class mock_arg_formatter
typedef fmt::internal::arg_formatter_base<buffer_range> base;
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()) {
EXPECT_CALL(*this, call(42));
}
@ -2167,7 +2168,7 @@ TEST(FormatTest, ConstexprParseFormatSpecs) {
struct test_parse_context {
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) {}
FMT_CONSTEXPR const char* begin() { return FMT_NULL; }
@ -2184,13 +2185,9 @@ struct test_context {
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>
FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id) {
return fmt::internal::make_arg<test_context>(22);
FMT_CONSTEXPR fmt::basic_format_arg<test_context> arg(Id id) {
return fmt::internal::make_arg<test_context>(id);
}
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("42").width() == 42, "");
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(".{}").precision == 11, "");
static_assert(parse_specs(".{0}").precision == 22, "");
static_assert(parse_specs(".{22}").precision == 22, "");
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("0").align() == fmt::ALIGN_NUMERIC, "");
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").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("d").type == 'd', "");
}

View File

@ -54,14 +54,15 @@ TEST(OStreamTest, Enum) {
typedef fmt::back_insert_range<fmt::internal::buffer> 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)
: fmt::arg_formatter<range>(ctx, &s) {}
: fmt::arg_formatter<range>(ctx, &parse_ctx, &s), parse_ctx("") {}
};
TEST(OStreamTest, CustomArg) {
fmt::memory_buffer 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;
test_arg_formatter af(ctx, spec);
fmt::visit_format_arg(