Add parse context

This commit is contained in:
Victor Zverovich 2017-09-17 08:32:57 -07:00
parent ec4f5175f1
commit 8cbf544733
6 changed files with 79 additions and 60 deletions

View File

@ -777,8 +777,9 @@ class null_terminating_iterator {
null_terminating_iterator(const Char *ptr, const Char *end) null_terminating_iterator(const Char *ptr, const Char *end)
: ptr_(ptr), end_(end) {} : ptr_(ptr), end_(end) {}
explicit null_terminating_iterator(basic_string_view<Char> s) template <typename Range>
: ptr_(s.begin()), end_(s.end()) {} explicit null_terminating_iterator(const Range &r)
: ptr_(r.begin()), end_(r.end()) {}
null_terminating_iterator &operator=(const Char *ptr) { null_terminating_iterator &operator=(const Char *ptr) {
assert(ptr <= end_); assert(ptr <= end_);
@ -1102,8 +1103,7 @@ struct string_value {
template <typename Char> template <typename Char>
struct custom_value { struct custom_value {
typedef void (*FormatFunc)( typedef void (*FormatFunc)(
basic_buffer<Char> &buffer, const void *arg, basic_buffer<Char> &buffer, const void *arg, void *ctx);
basic_string_view<Char>& format, void *ctx);
const void *value; const void *value;
FormatFunc format; FormatFunc format;
@ -1318,15 +1318,14 @@ class value {
// Formats an argument of a custom type, such as a user-defined class. // Formats an argument of a custom type, such as a user-defined class.
template <typename T> template <typename T>
static void format_custom_arg( static void format_custom_arg(
basic_buffer<char_type> &buffer, const void *arg, basic_buffer<char_type> &buffer, const void *arg, void *context) {
basic_string_view<char_type> &format, void *context) {
Context &ctx = *static_cast<Context*>(context); Context &ctx = *static_cast<Context*>(context);
// Get the formatter type through the context to allow different contexts // Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and // have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`. // `printf_formatter<T>` for `printf`.
typename Context::template formatter_type<T> f; typename Context::template formatter_type<T> f;
auto it = f.parse(format); auto &&parse_ctx = ctx.get_parse_context();
format.remove_prefix(it - format.begin()); parse_ctx.advance_to(f.parse(parse_ctx));
f.format(buffer, *static_cast<const T*>(arg), ctx); f.format(buffer, *static_cast<const T*>(arg), ctx);
} }
}; };
@ -1876,8 +1875,27 @@ class arg_formatter_base {
} }
}; };
template <typename Char>
class parse_context {
private:
basic_string_view<Char> format_str_;
public:
using char_type = Char;
explicit parse_context(basic_string_view<Char> format_str)
: format_str_(format_str) {}
const Char *begin() const { return format_str_.begin(); }
const Char *end() const { return format_str_.end(); }
void advance_to(const Char *p) {
format_str_.remove_prefix(p - begin());
}
};
template <typename Char, typename Context> template <typename Char, typename Context>
class context_base { class context_base : public parse_context<Char>{
private: private:
basic_args<Context> args_; basic_args<Context> args_;
int next_arg_index_; int next_arg_index_;
@ -1885,8 +1903,8 @@ class context_base {
protected: protected:
typedef basic_arg<Context> format_arg; typedef basic_arg<Context> format_arg;
explicit context_base(basic_args<Context> args) context_base(basic_string_view<Char> format_str, basic_args<Context> args)
: args_(args), next_arg_index_(0) {} : parse_context<Char>(format_str), args_(args), next_arg_index_(0) {}
~context_base() {} ~context_base() {}
basic_args<Context> args() const { return args_; } basic_args<Context> args() const { return args_; }
@ -1922,6 +1940,9 @@ class context_base {
next_arg_index_ = -1; next_arg_index_ = -1;
return true; return true;
} }
public:
parse_context<Char> &get_parse_context() { return *this; }
}; };
} // namespace internal } // namespace internal
@ -1952,8 +1973,7 @@ class arg_formatter : public internal::arg_formatter_base<Char> {
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
void operator()(internal::custom_value<Char> c) { void operator()(internal::custom_value<Char> c) {
basic_string_view<Char> format_str; c.format(this->writer().buffer(), c.value, &ctx_);
c.format(this->writer().buffer(), c.value, format_str, &ctx_);
} }
}; };
@ -1984,7 +2004,9 @@ class basic_context :
stored in the object so make sure they have appropriate lifetimes. stored in the object so make sure they have appropriate lifetimes.
\endrst \endrst
*/ */
basic_context(basic_args<basic_context> args): Base(args) {} basic_context(
basic_string_view<Char> format_str, basic_args<basic_context> args)
: Base(format_str, args) {}
format_arg next_arg() { format_arg next_arg() {
const char *error = 0; const char *error = 0;
@ -3001,16 +3023,14 @@ template <typename Char, typename Context>
class custom_formatter { class custom_formatter {
private: private:
basic_buffer<Char> &buffer_; basic_buffer<Char> &buffer_;
basic_string_view<Char> &format_;
Context &ctx_; Context &ctx_;
public: public:
custom_formatter(basic_buffer<Char> &buffer, basic_string_view<Char> &format, custom_formatter(basic_buffer<Char> &buffer,Context &ctx)
Context &ctx) : buffer_(buffer), ctx_(ctx) {}
: buffer_(buffer), format_(format), ctx_(ctx) {}
bool operator()(internal::custom_value<Char> custom) { bool operator()(internal::custom_value<Char> custom) {
custom.format(buffer_, custom.value, format_, &ctx_); custom.format(buffer_, custom.value, &ctx_);
return true; return true;
} }
@ -3257,16 +3277,16 @@ class dynamic_specs_handler :
} }
private: private:
using arg_ref = arg_ref<char_type>; using arg_ref_type = arg_ref<char_type>;
template <typename Id> template <typename Id>
arg_ref make_arg_ref(Id arg_id) { arg_ref_type make_arg_ref(Id arg_id) {
return arg_ref(arg_id); return arg_ref_type(arg_id);
} }
arg_ref make_arg_ref(auto_id) { arg_ref_type make_arg_ref(auto_id) {
// TODO: get next index from context // TODO: get next index from context
return arg_ref(arg_ref::NONE); return arg_ref_type(arg_ref_type::NONE);
} }
dynamic_format_specs<char_type> &specs_; dynamic_format_specs<char_type> &specs_;
@ -3422,17 +3442,16 @@ Iterator parse_format_specs(Iterator it, Handler &handler) {
template <typename ArgFormatter, typename Char, typename Context> template <typename ArgFormatter, typename Char, typename Context>
const Char *do_format_arg(basic_buffer<Char> &buffer, const Char *do_format_arg(basic_buffer<Char> &buffer,
const basic_arg<Context> &arg, const basic_arg<Context> &arg,
basic_string_view<Char> format,
Context &ctx) { Context &ctx) {
auto it = null_terminating_iterator<Char>(format); auto it = null_terminating_iterator<Char>(ctx);
basic_format_specs<Char> specs; basic_format_specs<Char> specs;
if (*it == ':') { if (*it == ':') {
format.remove_prefix(1); ctx.advance_to(pointer_from(++it));
if (visit(custom_formatter<Char, Context>(buffer, format, ctx), arg)) if (visit(custom_formatter<Char, Context>(buffer, ctx), arg))
return format.begin(); return ctx.begin();
specs_checker<specs_handler<Context>> specs_checker<specs_handler<Context>>
handler(specs_handler<Context>(specs, ctx), arg.type()); handler(specs_handler<Context>(specs, ctx), arg.type());
it = parse_format_specs(it + 1, handler); it = parse_format_specs(it, handler);
} }
if (*it != '}') if (*it != '}')
@ -3595,8 +3614,8 @@ inline typename basic_context<Char>::format_arg
template <typename ArgFormatter, typename Char, typename Context> template <typename ArgFormatter, typename Char, typename Context>
void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str, void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
basic_args<Context> args) { basic_args<Context> args) {
basic_context<Char> ctx(args); basic_context<Char> ctx(format_str, args);
auto start = internal::null_terminating_iterator<Char>(format_str); auto start = internal::null_terminating_iterator<Char>(ctx);
auto it = start; auto it = start;
using internal::pointer_from; using internal::pointer_from;
while (*it) { while (*it) {
@ -3626,8 +3645,8 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
} handler(ctx, arg); } handler(ctx, arg);
it = parse_arg_id(it, handler); it = parse_arg_id(it, handler);
format_str.remove_prefix(pointer_from(it) - format_str.data()); ctx.advance_to(pointer_from(it));
it = internal::do_format_arg<ArgFormatter>(buffer, arg, format_str, ctx); it = internal::do_format_arg<ArgFormatter>(buffer, arg, ctx);
if (*it != '}') if (*it != '}')
FMT_THROW(format_error(fmt::format("unknown format specifier"))); FMT_THROW(format_error(fmt::format("unknown format specifier")));
start = ++it; start = ++it;

View File

@ -14,7 +14,7 @@ FMT_FUNC int vfprintf(std::FILE *f, string_view format, printf_args args) {
} }
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
template void printf_context<char>::format(string_view, buffer &); template void printf_context<char>::format(buffer &);
template void printf_context<wchar_t>::format(wstring_view, wbuffer &); template void printf_context<wchar_t>::format(wbuffer &);
#endif #endif
} }

View File

@ -269,11 +269,10 @@ class printf_arg_formatter : public internal::arg_formatter_base<Char> {
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
void operator()(internal::custom_value<Char> c) { void operator()(internal::custom_value<Char> c) {
const Char format_str_data[] = {'}', '\0'}; const Char format_str[] = {'}', '\0'};
basic_string_view<Char> format_str = format_str_data;
auto args = basic_args<basic_context<Char>>(); auto args = basic_args<basic_context<Char>>();
basic_context<Char> ctx(args); basic_context<Char> ctx(basic_string_view<Char>(format_str), args);
c.format(this->writer().buffer(), c.value, format_str, &ctx); c.format(this->writer().buffer(), c.value, &ctx);
} }
}; };
@ -283,9 +282,8 @@ class printf_context;
template <typename T, typename Char = char> template <typename T, typename Char = char>
struct printf_formatter { struct printf_formatter {
const Char *parse(basic_string_view<Char> s) { template <typename ParseContext>
return s.data(); const Char *parse(ParseContext& ctx) { return ctx.begin(); }
}
void format(basic_buffer<Char> &buf, const T &value, printf_context<Char> &) { void format(basic_buffer<Char> &buf, const T &value, printf_context<Char> &) {
internal::format_value(buf, value); internal::format_value(buf, value);
@ -329,11 +327,14 @@ class printf_context :
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
explicit printf_context(basic_args<printf_context> args): Base(args) {} printf_context(
basic_string_view<Char> format_str, basic_args<printf_context> args)
: Base(format_str, args) {}
using Base::get_parse_context;
/** Formats stored arguments and writes the output to the buffer. */ /** Formats stored arguments and writes the output to the buffer. */
FMT_API void format( FMT_API void format(basic_buffer<Char> &buffer);
basic_string_view<Char> format_str, basic_buffer<Char> &buffer);
}; };
template <typename Char, typename AF> template <typename Char, typename AF>
@ -415,9 +416,9 @@ unsigned printf_context<Char, AF>::parse_header(
} }
template <typename Char, typename AF> template <typename Char, typename AF>
void printf_context<Char, AF>::format( void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
basic_string_view<Char> format_str, basic_buffer<Char> &buffer) { Base &base = *this;
auto start = iterator(format_str); auto start = iterator(base);
auto it = start; auto it = start;
using internal::pointer_from; using internal::pointer_from;
while (*it) { while (*it) {
@ -519,7 +520,7 @@ void printf_context<Char, AF>::format(
template <typename Char> template <typename Char>
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format, void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
basic_args<printf_context<Char>> args) { basic_args<printf_context<Char>> args) {
printf_context<Char>(args).format(format, buf); printf_context<Char>(format, args).format(buf);
} }
typedef basic_args<printf_context<char>> printf_args; typedef basic_args<printf_context<char>> printf_args;

View File

@ -65,8 +65,8 @@ std::string custom_vsprintf(
const char* format_str, const char* format_str,
fmt::basic_args<CustomPrintfFormatter> args) { fmt::basic_args<CustomPrintfFormatter> args) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
CustomPrintfFormatter formatter(args); CustomPrintfFormatter formatter(format_str, args);
formatter.format(format_str, buffer); formatter.format(buffer);
return std::string(buffer.data(), buffer.size()); return std::string(buffer.data(), buffer.size());
} }

View File

@ -65,7 +65,7 @@ struct TestArgFormatter : fmt::arg_formatter<char> {
TEST(OStreamTest, CustomArg) { TEST(OStreamTest, CustomArg) {
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::context ctx((fmt::args())); fmt::context ctx("", fmt::args());
fmt::format_specs spec; fmt::format_specs spec;
TestArgFormatter af(buffer, ctx, spec); TestArgFormatter af(buffer, ctx, spec);
visit(af, fmt::internal::make_arg<fmt::context>(TestEnum())); visit(af, fmt::internal::make_arg<fmt::context>(TestEnum()));

View File

@ -448,8 +448,9 @@ struct CustomContext {
bool called; bool called;
fmt::string_view format() { return ""; } fmt::internal::parse_context<char> get_parse_context() {
void advance_to(const char *) {} return fmt::internal::parse_context<char>("");
}
}; };
TEST(UtilTest, MakeValueWithCustomFormatter) { TEST(UtilTest, MakeValueWithCustomFormatter) {
@ -457,8 +458,7 @@ TEST(UtilTest, MakeValueWithCustomFormatter) {
fmt::internal::value<CustomContext> arg(t); fmt::internal::value<CustomContext> arg(t);
CustomContext ctx = {false}; CustomContext ctx = {false};
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::string_view format_str; arg.custom.format(buffer, &t, &ctx);
arg.custom.format(buffer, &t, format_str, &ctx);
EXPECT_TRUE(ctx.called); EXPECT_TRUE(ctx.called);
} }
@ -602,9 +602,8 @@ TEST(UtilTest, CustomArg) {
testing::Invoke([&](fmt::internal::custom_value<char> custom) { testing::Invoke([&](fmt::internal::custom_value<char> custom) {
EXPECT_EQ(&test, custom.value); EXPECT_EQ(&test, custom.value);
fmt::memory_buffer buffer; fmt::memory_buffer buffer;
fmt::context ctx((fmt::args())); fmt::context ctx("", fmt::args());
fmt::string_view format_str; custom.format(buffer, &test, &ctx);
custom.format(buffer, &test, format_str, &ctx);
EXPECT_EQ("test", std::string(buffer.data(), buffer.size())); EXPECT_EQ("test", std::string(buffer.data(), buffer.size()));
return Visitor::Result(); return Visitor::Result();
})); }));