diff --git a/fmt/format.h b/fmt/format.h index c1f723ac..98f33e13 100644 --- a/fmt/format.h +++ b/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 s) - : ptr_(s.begin()), end_(s.end()) {} + template + 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 struct custom_value { typedef void (*FormatFunc)( - basic_buffer &buffer, const void *arg, - basic_string_view& format, void *ctx); + basic_buffer &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 static void format_custom_arg( - basic_buffer &buffer, const void *arg, - basic_string_view &format, void *context) { + basic_buffer &buffer, const void *arg, void *context) { Context &ctx = *static_cast(context); // Get the formatter type through the context to allow different contexts // have different extension points, e.g. `formatter` for `format` and // `printf_formatter` for `printf`. typename Context::template formatter_type 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(arg), ctx); } }; @@ -1876,8 +1875,27 @@ class arg_formatter_base { } }; +template +class parse_context { + private: + basic_string_view format_str_; + + public: + using char_type = Char; + + explicit parse_context(basic_string_view 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 -class context_base { +class context_base : public parse_context{ private: basic_args args_; int next_arg_index_; @@ -1885,8 +1903,8 @@ class context_base { protected: typedef basic_arg format_arg; - explicit context_base(basic_args args) - : args_(args), next_arg_index_(0) {} + context_base(basic_string_view format_str, basic_args args) + : parse_context(format_str), args_(args), next_arg_index_(0) {} ~context_base() {} basic_args args() const { return args_; } @@ -1922,6 +1940,9 @@ class context_base { next_arg_index_ = -1; return true; } + + public: + parse_context &get_parse_context() { return *this; } }; } // namespace internal @@ -1952,8 +1973,7 @@ class arg_formatter : public internal::arg_formatter_base { /** Formats an argument of a custom (user-defined) type. */ void operator()(internal::custom_value c) { - basic_string_view 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 args): Base(args) {} + basic_context( + basic_string_view format_str, basic_args args) + : Base(format_str, args) {} format_arg next_arg() { const char *error = 0; @@ -3001,16 +3023,14 @@ template class custom_formatter { private: basic_buffer &buffer_; - basic_string_view &format_; Context &ctx_; public: - custom_formatter(basic_buffer &buffer, basic_string_view &format, - Context &ctx) - : buffer_(buffer), format_(format), ctx_(ctx) {} + custom_formatter(basic_buffer &buffer,Context &ctx) + : buffer_(buffer), ctx_(ctx) {} bool operator()(internal::custom_value 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; + using arg_ref_type = arg_ref; template - 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 &specs_; @@ -3422,17 +3442,16 @@ Iterator parse_format_specs(Iterator it, Handler &handler) { template const Char *do_format_arg(basic_buffer &buffer, const basic_arg &arg, - basic_string_view format, Context &ctx) { - auto it = null_terminating_iterator(format); + auto it = null_terminating_iterator(ctx); basic_format_specs specs; if (*it == ':') { - format.remove_prefix(1); - if (visit(custom_formatter(buffer, format, ctx), arg)) - return format.begin(); + ctx.advance_to(pointer_from(++it)); + if (visit(custom_formatter(buffer, ctx), arg)) + return ctx.begin(); specs_checker> handler(specs_handler(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::format_arg template void vformat_to(basic_buffer &buffer, basic_string_view format_str, basic_args args) { - basic_context ctx(args); - auto start = internal::null_terminating_iterator(format_str); + basic_context ctx(format_str, args); + auto start = internal::null_terminating_iterator(ctx); auto it = start; using internal::pointer_from; while (*it) { @@ -3626,8 +3645,8 @@ void vformat_to(basic_buffer &buffer, basic_string_view 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(buffer, arg, format_str, ctx); + ctx.advance_to(pointer_from(it)); + it = internal::do_format_arg(buffer, arg, ctx); if (*it != '}') FMT_THROW(format_error(fmt::format("unknown format specifier"))); start = ++it; diff --git a/fmt/printf.cc b/fmt/printf.cc index c107c46b..53d4c0b9 100644 --- a/fmt/printf.cc +++ b/fmt/printf.cc @@ -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::format(string_view, buffer &); -template void printf_context::format(wstring_view, wbuffer &); +template void printf_context::format(buffer &); +template void printf_context::format(wbuffer &); #endif } diff --git a/fmt/printf.h b/fmt/printf.h index ac4b33de..eb9e39db 100644 --- a/fmt/printf.h +++ b/fmt/printf.h @@ -269,11 +269,10 @@ class printf_arg_formatter : public internal::arg_formatter_base { /** Formats an argument of a custom (user-defined) type. */ void operator()(internal::custom_value c) { - const Char format_str_data[] = {'}', '\0'}; - basic_string_view format_str = format_str_data; + const Char format_str[] = {'}', '\0'}; auto args = basic_args>(); - basic_context ctx(args); - c.format(this->writer().buffer(), c.value, format_str, &ctx); + basic_context ctx(basic_string_view(format_str), args); + c.format(this->writer().buffer(), c.value, &ctx); } }; @@ -283,9 +282,8 @@ class printf_context; template struct printf_formatter { - const Char *parse(basic_string_view s) { - return s.data(); - } + template + const Char *parse(ParseContext& ctx) { return ctx.begin(); } void format(basic_buffer &buf, const T &value, printf_context &) { internal::format_value(buf, value); @@ -329,11 +327,14 @@ class printf_context : appropriate lifetimes. \endrst */ - explicit printf_context(basic_args args): Base(args) {} + printf_context( + basic_string_view format_str, basic_args 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 format_str, basic_buffer &buffer); + FMT_API void format(basic_buffer &buffer); }; template @@ -415,9 +416,9 @@ unsigned printf_context::parse_header( } template -void printf_context::format( - basic_string_view format_str, basic_buffer &buffer) { - auto start = iterator(format_str); +void printf_context::format(basic_buffer &buffer) { + Base &base = *this; + auto start = iterator(base); auto it = start; using internal::pointer_from; while (*it) { @@ -519,7 +520,7 @@ void printf_context::format( template void printf(basic_buffer &buf, basic_string_view format, basic_args> args) { - printf_context(args).format(format, buf); + printf_context(format, args).format(buf); } typedef basic_args> printf_args; diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index b84db61e..e189fa4d 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -65,8 +65,8 @@ std::string custom_vsprintf( const char* format_str, fmt::basic_args 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()); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index b5deb6ad..cff83272 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -65,7 +65,7 @@ struct TestArgFormatter : fmt::arg_formatter { 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(TestEnum())); diff --git a/test/util-test.cc b/test/util-test.cc index c3387639..5c276e31 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -448,8 +448,9 @@ struct CustomContext { bool called; - fmt::string_view format() { return ""; } - void advance_to(const char *) {} + fmt::internal::parse_context get_parse_context() { + return fmt::internal::parse_context(""); + } }; TEST(UtilTest, MakeValueWithCustomFormatter) { @@ -457,8 +458,7 @@ TEST(UtilTest, MakeValueWithCustomFormatter) { fmt::internal::value 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 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(); }));