diff --git a/include/fmt/core.h b/include/fmt/core.h index 92797da3..a8a33c10 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -568,8 +568,8 @@ class value { // iostreams. template void set_pointer(T *p) { - using type = typename std::remove_const::type; - static_assert(std::is_same::value, + using nonconst_type = typename std::remove_const::type; + static_assert(std::is_same::value, "formatting of non-void pointers is disallowed"); set(pointer, p); } @@ -583,7 +583,7 @@ class value { typename Context::template formatter_type f; auto &&parse_ctx = ctx.parse_context(); parse_ctx.advance_to(f.parse(parse_ctx)); - f.format(*static_cast(arg), ctx); + ctx.advance_to(f.format(*static_cast(arg), ctx)); } }; @@ -710,6 +710,7 @@ inline typename std::enable_if>::type return make_arg(value); } +// A map from argument names to their values for named arguments. template class arg_map { private: @@ -748,9 +749,13 @@ class arg_map { template class context_base { + public: + using iterator = decltype(std::declval().begin()); + private: basic_parse_context parse_context_; Range range_; + iterator out_; basic_format_args args_; protected: @@ -790,6 +795,12 @@ class context_base { void on_error(const char *message) { parse_context_.on_error(message); } Range range() { return range_; } + + // Returns an iterator to the beginning of the output range. + iterator begin() { return out_; } + + // Advances the begin iterator to ``it``. + void advance_to(iterator it) { out_ = it; } }; // A range that can grow dynamically. @@ -833,6 +844,8 @@ class basic_context : template using formatter_type = formatter; + using range_type = Range; + private: internal::arg_map map_; @@ -843,6 +856,8 @@ class basic_context : using base::get_arg; public: + using typename base::iterator; + /** \rst Constructs a ``basic_context`` object. References to the arguments are @@ -858,7 +873,7 @@ class basic_context : } format_arg get_arg(unsigned arg_id) { return this->do_get_arg(arg_id); } - // Checks if manual indexing is used and returns the argument with + // Checks if manual indexing is used and returns the argument with the // specified name. format_arg get_arg(basic_string_view name); }; @@ -878,7 +893,7 @@ class arg_store { IS_PACKED, internal::value, basic_arg>::type; // If the arguments are not packed, add one more element to mark the end. - value_type data_[NUM_ARGS + (IS_PACKED ? 0 : 1)]; + value_type data_[NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1)]; public: static const uint64_t TYPES = IS_PACKED ? @@ -984,6 +999,8 @@ struct named_arg_base { // Serialized value. mutable char data[sizeof(basic_arg)]; + named_arg_base(basic_string_view name) : name(name) {} + template basic_arg deserialize() const { basic_arg arg; @@ -997,7 +1014,7 @@ struct named_arg : named_arg_base { const T &value; named_arg(basic_string_view name, const T &val) - : named_arg_base{name}, value(val) {} + : named_arg_base(name), value(val) {} }; } diff --git a/include/fmt/format.h b/include/fmt/format.h index f78294cc..47149cde 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2789,14 +2789,16 @@ struct formatter< return pointer_from(it); } - template - void format(const T &val, basic_context &ctx) { + template + typename FormatContext::iterator format(const T &val, FormatContext &ctx) { internal::handle_dynamic_spec( specs_.width_, specs_.width_ref, ctx); internal::handle_dynamic_spec( specs_.precision_, specs_.precision_ref, ctx); - visit(arg_formatter(ctx.range(), ctx, specs_), - internal::make_arg>(val)); + using range = typename FormatContext::range_type; + visit(arg_formatter(ctx.range(), ctx, specs_), + internal::make_arg(val)); + return ctx.begin(); } private: @@ -2834,8 +2836,8 @@ struct dynamic_formatter { return pointer_from(it); } - template - void format(const T &val, basic_context &ctx) { + template + auto format(const T &val, FormatContext &ctx) -> decltype(ctx.begin()) { handle_specs(ctx); struct null_handler : internal::error_handler { void on_align(alignment) {} @@ -2861,8 +2863,10 @@ struct dynamic_formatter { } if (specs_.precision_ != -1) checker.end_precision(); - visit(arg_formatter(ctx.range(), ctx, specs_), - internal::make_arg>(val)); + using range = typename FormatContext::range_type; + visit(arg_formatter(ctx.range(), ctx, specs_), + internal::make_arg(val)); + return ctx.begin(); } private: diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 72b28a6f..6ed59801 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -103,11 +103,12 @@ struct formatter, Char> { template - void format(const T &value, Context &ctx) { + auto format(const T &value, Context &ctx) -> decltype(ctx.begin()) { basic_memory_buffer buffer; internal::format_value(buffer, value); basic_string_view str(buffer.data(), buffer.size()); formatter, Char>::format(str, ctx); + return ctx.begin(); } }; diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 24019082..eadcabea 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -285,11 +285,12 @@ class printf_arg_formatter : public internal::arg_formatter_base { template struct printf_formatter { template - auto parse(ParseContext& ctx) { return ctx.begin(); } + auto parse(ParseContext &ctx) { return ctx.begin(); } - template - void format(const T &value, basic_printf_context &ctx) { + template + auto format(const T &value, FormatContext &ctx) -> decltype(ctx.begin()) { internal::format_value(ctx.range().container(), value); + return ctx.begin(); } }; @@ -306,8 +307,8 @@ class basic_printf_context : using formatter_type = printf_formatter; private: - typedef internal::context_base Base; - using format_arg = typename Base::format_arg; + using base = internal::context_base; + using format_arg = typename base::format_arg; using format_specs = basic_format_specs; using iterator = internal::null_terminating_iterator; @@ -332,10 +333,12 @@ class basic_printf_context : */ basic_printf_context(Range range, basic_string_view format_str, basic_format_args args) - : Base(range, format_str, args) {} + : base(range, format_str, args) {} - using Base::parse_context; - using Base::range; + using base::parse_context; + using base::range; + using base::begin; + using base::advance_to; /** Formats stored arguments and writes the output to the range. */ FMT_API void format(); @@ -374,7 +377,7 @@ typename basic_printf_context::format_arg (void)it; if (arg_index == std::numeric_limits::max()) return this->do_get_arg(this->parse_context().next_arg_id()); - return Base::get_arg(arg_index - 1); + return base::get_arg(arg_index - 1); } template diff --git a/include/fmt/time.h b/include/fmt/time.h index f0faca46..12d9bd3a 100644 --- a/include/fmt/time.h +++ b/include/fmt/time.h @@ -30,7 +30,7 @@ struct formatter { return pointer_from(end); } - void format(const std::tm &tm, context &ctx) { + auto format(const std::tm &tm, context &ctx) -> decltype(ctx.begin()) { buffer &buf = ctx.range().container(); std::size_t start = buf.size(); for (;;) { @@ -50,6 +50,7 @@ struct formatter { const std::size_t MIN_GROWTH = 10; buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); } + return ctx.begin(); } memory_buffer tm_format; diff --git a/test/format-test.cc b/test/format-test.cc index f728faeb..6296ad61 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1223,8 +1223,9 @@ struct formatter { return it; } - void format(const Date &d, context &ctx) { + auto format(const Date &d, context &ctx) { format_range(ctx.range(), "{}-{}-{}", d.year(), d.month(), d.day()); + return ctx.begin(); } }; } @@ -1240,8 +1241,8 @@ class Answer {}; namespace fmt { template <> struct formatter : formatter { - void format(Answer, fmt::context &ctx) { - formatter::format(42, ctx); + auto format(Answer, fmt::context &ctx) { + return formatter::format(42, ctx); } }; } @@ -1534,11 +1535,10 @@ struct variant { namespace fmt { template <> struct formatter : dynamic_formatter<> { - void format(variant value, context& ctx) { + auto format(variant value, context& ctx) { if (value.type == variant::INT) - dynamic_formatter::format(42, ctx); - else - dynamic_formatter::format("foo", ctx); + return dynamic_formatter::format(42, ctx); + return dynamic_formatter::format("foo", ctx); } }; } diff --git a/test/util-test.cc b/test/util-test.cc index 90904868..d23107ab 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -83,9 +83,9 @@ struct formatter { using range = fmt::internal::dynamic_range>; - void format(Test, basic_context &ctx) { + auto format(Test, basic_context &ctx) -> decltype(ctx.begin()) { const Char *test = "test"; - ctx.range().container().append(test, test + std::strlen(test)); + return std::copy_n(test, std::strlen(test), ctx.begin()); } }; } @@ -434,7 +434,7 @@ TEST(UtilTest, FormatArgs) { EXPECT_FALSE(args[1]); } -struct CustomContext { +struct custom_context { using char_type = char; template @@ -444,20 +444,22 @@ struct CustomContext { return ctx.begin(); } - void format(const T &, CustomContext& ctx) { + const char *format(const T &, custom_context& ctx) { ctx.called = true; + return 0; } }; bool called; fmt::parse_context parse_context() { return fmt::parse_context(""); } + void advance_to(const char *) {} }; TEST(UtilTest, MakeValueWithCustomFormatter) { ::Test t; - fmt::internal::value arg(t); - CustomContext ctx = {false}; + fmt::internal::value arg(t); + custom_context ctx = {false}; arg.custom.format(&t, ctx); EXPECT_TRUE(ctx.called); }