mirror of
https://github.com/fmtlib/fmt.git
synced 2025-02-14 00:40:08 +00:00
Add parse context
This commit is contained in:
parent
ec4f5175f1
commit
8cbf544733
87
fmt/format.h
87
fmt/format.h
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
29
fmt/printf.h
29
fmt/printf.h
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
|
@ -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();
|
||||
}));
|
||||
|
Loading…
x
Reference in New Issue
Block a user