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)
: ptr_(ptr), end_(end) {}
explicit null_terminating_iterator(basic_string_view<Char> s)
: ptr_(s.begin()), end_(s.end()) {}
template <typename Range>
explicit null_terminating_iterator(const Range &r)
: ptr_(r.begin()), end_(r.end()) {}
null_terminating_iterator &operator=(const Char *ptr) {
assert(ptr <= end_);
@ -1102,8 +1103,7 @@ struct string_value {
template <typename Char>
struct custom_value {
typedef void (*FormatFunc)(
basic_buffer<Char> &buffer, const void *arg,
basic_string_view<Char>& format, void *ctx);
basic_buffer<Char> &buffer, const void *arg, void *ctx);
const void *value;
FormatFunc format;
@ -1318,15 +1318,14 @@ class value {
// Formats an argument of a custom type, such as a user-defined class.
template <typename T>
static void format_custom_arg(
basic_buffer<char_type> &buffer, const void *arg,
basic_string_view<char_type> &format, void *context) {
basic_buffer<char_type> &buffer, const void *arg, void *context) {
Context &ctx = *static_cast<Context*>(context);
// Get the formatter type through the context to allow different contexts
// have different extension points, e.g. `formatter<T>` for `format` and
// `printf_formatter<T>` for `printf`.
typename Context::template formatter_type<T> f;
auto it = f.parse(format);
format.remove_prefix(it - format.begin());
auto &&parse_ctx = ctx.get_parse_context();
parse_ctx.advance_to(f.parse(parse_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>
class context_base {
class context_base : public parse_context<Char>{
private:
basic_args<Context> args_;
int next_arg_index_;
@ -1885,8 +1903,8 @@ class context_base {
protected:
typedef basic_arg<Context> format_arg;
explicit context_base(basic_args<Context> args)
: args_(args), next_arg_index_(0) {}
context_base(basic_string_view<Char> format_str, basic_args<Context> args)
: parse_context<Char>(format_str), args_(args), next_arg_index_(0) {}
~context_base() {}
basic_args<Context> args() const { return args_; }
@ -1922,6 +1940,9 @@ class context_base {
next_arg_index_ = -1;
return true;
}
public:
parse_context<Char> &get_parse_context() { return *this; }
};
} // namespace internal
@ -1952,8 +1973,7 @@ class arg_formatter : public internal::arg_formatter_base<Char> {
/** Formats an argument of a custom (user-defined) type. */
void operator()(internal::custom_value<Char> c) {
basic_string_view<Char> format_str;
c.format(this->writer().buffer(), c.value, format_str, &ctx_);
c.format(this->writer().buffer(), c.value, &ctx_);
}
};
@ -1984,7 +2004,9 @@ class basic_context :
stored in the object so make sure they have appropriate lifetimes.
\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() {
const char *error = 0;
@ -3001,16 +3023,14 @@ template <typename Char, typename Context>
class custom_formatter {
private:
basic_buffer<Char> &buffer_;
basic_string_view<Char> &format_;
Context &ctx_;
public:
custom_formatter(basic_buffer<Char> &buffer, basic_string_view<Char> &format,
Context &ctx)
: buffer_(buffer), format_(format), ctx_(ctx) {}
custom_formatter(basic_buffer<Char> &buffer,Context &ctx)
: buffer_(buffer), ctx_(ctx) {}
bool operator()(internal::custom_value<Char> custom) {
custom.format(buffer_, custom.value, format_, &ctx_);
custom.format(buffer_, custom.value, &ctx_);
return true;
}
@ -3257,16 +3277,16 @@ class dynamic_specs_handler :
}
private:
using arg_ref = arg_ref<char_type>;
using arg_ref_type = arg_ref<char_type>;
template <typename Id>
arg_ref make_arg_ref(Id arg_id) {
return arg_ref(arg_id);
arg_ref_type make_arg_ref(Id 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
return arg_ref(arg_ref::NONE);
return arg_ref_type(arg_ref_type::NONE);
}
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>
const Char *do_format_arg(basic_buffer<Char> &buffer,
const basic_arg<Context> &arg,
basic_string_view<Char> format,
Context &ctx) {
auto it = null_terminating_iterator<Char>(format);
auto it = null_terminating_iterator<Char>(ctx);
basic_format_specs<Char> specs;
if (*it == ':') {
format.remove_prefix(1);
if (visit(custom_formatter<Char, Context>(buffer, format, ctx), arg))
return format.begin();
ctx.advance_to(pointer_from(++it));
if (visit(custom_formatter<Char, Context>(buffer, ctx), arg))
return ctx.begin();
specs_checker<specs_handler<Context>>
handler(specs_handler<Context>(specs, ctx), arg.type());
it = parse_format_specs(it + 1, handler);
it = parse_format_specs(it, handler);
}
if (*it != '}')
@ -3595,8 +3614,8 @@ inline typename basic_context<Char>::format_arg
template <typename ArgFormatter, typename Char, typename Context>
void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
basic_args<Context> args) {
basic_context<Char> ctx(args);
auto start = internal::null_terminating_iterator<Char>(format_str);
basic_context<Char> ctx(format_str, args);
auto start = internal::null_terminating_iterator<Char>(ctx);
auto it = start;
using internal::pointer_from;
while (*it) {
@ -3626,8 +3645,8 @@ void vformat_to(basic_buffer<Char> &buffer, basic_string_view<Char> format_str,
} handler(ctx, arg);
it = parse_arg_id(it, handler);
format_str.remove_prefix(pointer_from(it) - format_str.data());
it = internal::do_format_arg<ArgFormatter>(buffer, arg, format_str, ctx);
ctx.advance_to(pointer_from(it));
it = internal::do_format_arg<ArgFormatter>(buffer, arg, ctx);
if (*it != '}')
FMT_THROW(format_error(fmt::format("unknown format specifier")));
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
template void printf_context<char>::format(string_view, buffer &);
template void printf_context<wchar_t>::format(wstring_view, wbuffer &);
template void printf_context<char>::format(buffer &);
template void printf_context<wchar_t>::format(wbuffer &);
#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. */
void operator()(internal::custom_value<Char> c) {
const Char format_str_data[] = {'}', '\0'};
basic_string_view<Char> format_str = format_str_data;
const Char format_str[] = {'}', '\0'};
auto args = basic_args<basic_context<Char>>();
basic_context<Char> ctx(args);
c.format(this->writer().buffer(), c.value, format_str, &ctx);
basic_context<Char> ctx(basic_string_view<Char>(format_str), args);
c.format(this->writer().buffer(), c.value, &ctx);
}
};
@ -283,9 +282,8 @@ class printf_context;
template <typename T, typename Char = char>
struct printf_formatter {
const Char *parse(basic_string_view<Char> s) {
return s.data();
}
template <typename ParseContext>
const Char *parse(ParseContext& ctx) { return ctx.begin(); }
void format(basic_buffer<Char> &buf, const T &value, printf_context<Char> &) {
internal::format_value(buf, value);
@ -329,11 +327,14 @@ class printf_context :
appropriate lifetimes.
\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. */
FMT_API void format(
basic_string_view<Char> format_str, basic_buffer<Char> &buffer);
FMT_API void format(basic_buffer<Char> &buffer);
};
template <typename Char, typename AF>
@ -415,9 +416,9 @@ unsigned printf_context<Char, AF>::parse_header(
}
template <typename Char, typename AF>
void printf_context<Char, AF>::format(
basic_string_view<Char> format_str, basic_buffer<Char> &buffer) {
auto start = iterator(format_str);
void printf_context<Char, AF>::format(basic_buffer<Char> &buffer) {
Base &base = *this;
auto start = iterator(base);
auto it = start;
using internal::pointer_from;
while (*it) {
@ -519,7 +520,7 @@ void printf_context<Char, AF>::format(
template <typename Char>
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
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;

View File

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

View File

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

View File

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