diff --git a/include/fmt/core.h b/include/fmt/core.h index b1bf7b9c..d7807baa 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -226,7 +226,7 @@ struct result_of : std::invoke_result {}; // A workaround for gcc 4.4 that doesn't allow F to be a reference. template struct result_of - : std::result_of::type(Args...)> {}; + : std::result_of::type(Args...)> {}; #endif // Casts nonnegative integer to unsigned. @@ -518,6 +518,63 @@ FMT_CONSTEXPR basic_string_view to_string_view( return s; } +// Parsing context consisting of a format string range being parsed and an +// argument counter for automatic indexing. +template +class basic_parse_context : private ErrorHandler { + private: + basic_string_view format_str_; + int next_arg_id_; + + public: + typedef Char char_type; + typedef typename basic_string_view::iterator iterator; + + explicit FMT_CONSTEXPR basic_parse_context(basic_string_view format_str, + ErrorHandler eh = ErrorHandler()) + : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} + + // Returns an iterator to the beginning of the format string range being + // parsed. + FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT { + return format_str_.begin(); + } + + // Returns an iterator past the end of the format string range being parsed. + FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); } + + // Advances the begin iterator to ``it``. + FMT_CONSTEXPR void advance_to(iterator it) { + format_str_.remove_prefix(internal::to_unsigned(it - begin())); + } + + // Returns the next argument index. + FMT_CONSTEXPR unsigned next_arg_id(); + + FMT_CONSTEXPR bool check_arg_id(unsigned) { + if (next_arg_id_ > 0) { + on_error("cannot switch from automatic to manual argument indexing"); + return false; + } + next_arg_id_ = -1; + return true; + } + + FMT_CONSTEXPR void check_arg_id(basic_string_view) {} + + FMT_CONSTEXPR void on_error(const char* message) { + ErrorHandler::on_error(message); + } + + FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } +}; + +typedef basic_parse_context format_parse_context; +typedef basic_parse_context wformat_parse_context; + +FMT_DEPRECATED typedef basic_parse_context parse_context; +FMT_DEPRECATED typedef basic_parse_context wparse_context; + template class basic_format_arg; template class basic_format_args; @@ -605,7 +662,9 @@ template struct string_value { template struct custom_value { const void* value; - void (*format)(const void* arg, Context& ctx); + void (*format)(const void* arg, + basic_parse_context& parse_ctx, + Context& ctx); }; // A formatting argument value. @@ -673,9 +732,10 @@ template class value { private: // Formats an argument of a custom type, such as a user-defined class. template - static void format_custom_arg(const void* arg, Context& ctx) { + static void format_custom_arg(const void* arg, + basic_parse_context& parse_ctx, + Context& ctx) { Formatter f; - auto&& parse_ctx = ctx.parse_context(); parse_ctx.advance_to(f.parse(parse_ctx)); ctx.advance_to(f.format(*static_cast(arg), ctx)); } @@ -866,7 +926,9 @@ template class basic_format_arg { public: explicit handle(internal::custom_value custom) : custom_(custom) {} - void format(Context& ctx) const { custom_.format(custom_.value, ctx); } + void format(basic_parse_context& parse_ctx, Context& ctx) const { + custom_.format(custom_.value, parse_ctx, ctx); + } private: internal::custom_value custom_; @@ -938,63 +1000,6 @@ visit(Visitor&& vis, const basic_format_arg& arg) { return visit_format_arg(std::forward(vis), arg); } -// Parsing context consisting of a format string range being parsed and an -// argument counter for automatic indexing. -template -class basic_parse_context : private ErrorHandler { - private: - basic_string_view format_str_; - int next_arg_id_; - - public: - typedef Char char_type; - typedef typename basic_string_view::iterator iterator; - - explicit FMT_CONSTEXPR basic_parse_context(basic_string_view format_str, - ErrorHandler eh = ErrorHandler()) - : ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {} - - // Returns an iterator to the beginning of the format string range being - // parsed. - FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT { - return format_str_.begin(); - } - - // Returns an iterator past the end of the format string range being parsed. - FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); } - - // Advances the begin iterator to ``it``. - FMT_CONSTEXPR void advance_to(iterator it) { - format_str_.remove_prefix(internal::to_unsigned(it - begin())); - } - - // Returns the next argument index. - FMT_CONSTEXPR unsigned next_arg_id(); - - FMT_CONSTEXPR bool check_arg_id(unsigned) { - if (next_arg_id_ > 0) { - on_error("cannot switch from automatic to manual argument indexing"); - return false; - } - next_arg_id_ = -1; - return true; - } - - FMT_CONSTEXPR void check_arg_id(basic_string_view) {} - - FMT_CONSTEXPR void on_error(const char* message) { - ErrorHandler::on_error(message); - } - - FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; } -}; - -typedef basic_parse_context format_parse_context; -typedef basic_parse_context wformat_parse_context; - -FMT_DEPRECATED typedef basic_parse_context parse_context; -FMT_DEPRECATED typedef basic_parse_context wparse_context; - namespace internal { // A map from argument names to their values for named arguments. template class arg_map { @@ -1073,15 +1078,21 @@ class context_base { return arg; } + friend basic_parse_context& get_parse_context(context_base& ctx) { + return ctx.parse_context_; + } + // Checks if manual indexing is used and returns the argument with // specified index. format_arg arg(unsigned arg_id) { - return this->parse_context().check_arg_id(arg_id) ? this->do_get_arg(arg_id) - : format_arg(); + return parse_context_.check_arg_id(arg_id) ? this->do_get_arg(arg_id) + : format_arg(); } public: - basic_parse_context& parse_context() { return parse_context_; } + FMT_DEPRECATED basic_parse_context& parse_context() { + return parse_context_; + } // basic_format_context::arg() depends on this // Cannot be marked as deprecated without causing warnings @@ -1178,7 +1189,7 @@ class basic_format_context : base(out, format_str, ctx_args, loc) {} format_arg next_arg() { - return this->do_get_arg(this->parse_context().next_arg_id()); + return this->do_get_arg(get_parse_context(*this).next_arg_id()); } format_arg arg(unsigned arg_id) { return this->do_get_arg(arg_id); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 2d02e59b..2507e68c 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1576,7 +1576,7 @@ class custom_formatter : public function { explicit custom_formatter(Context& ctx) : ctx_(ctx) {} bool operator()(typename basic_format_arg::handle h) const { - h.format(ctx_); + h.format(get_parse_context(ctx_), ctx_); return true; } @@ -1762,14 +1762,14 @@ FMT_CONSTEXPR void set_dynamic_spec(T& value, FormatArg arg, ErrorHandler eh) { struct auto_id {}; // The standard format specifier handler with checking. -template +template class specs_handler : public specs_setter { public: typedef typename Context::char_type char_type; FMT_CONSTEXPR specs_handler(basic_format_specs& specs, - Context& ctx) - : specs_setter(specs), context_(ctx) {} + ParseContext& parse_ctx, Context& ctx) + : specs_setter(specs), parse_ctx_(parse_ctx), context_(ctx) {} template FMT_CONSTEXPR void on_dynamic_width(Id arg_id) { set_dynamic_spec(this->specs_.width_, get_arg(arg_id), @@ -1790,10 +1790,11 @@ class specs_handler : public specs_setter { FMT_CONSTEXPR format_arg get_arg(auto_id) { return context_.next_arg(); } template FMT_CONSTEXPR format_arg get_arg(Id arg_id) { - context_.parse_context().check_arg_id(arg_id); + parse_ctx_.check_arg_id(arg_id); return context_.arg(arg_id); } + ParseContext& parse_ctx_; Context& context_; }; @@ -2273,7 +2274,7 @@ void handle_dynamic_spec(Spec& value, arg_ref ref, ctx.error_handler()); break; case arg_ref::NAME: { - const auto arg_id = ref.val.name.to_view(ctx.parse_context().begin()); + const auto arg_id = ref.val.name.to_view(get_parse_context(ctx).begin()); internal::set_dynamic_spec(value, ctx.arg(arg_id), ctx.error_handler()); } break; @@ -2316,7 +2317,7 @@ class arg_formatter /** Formats an argument of a user-defined type. */ iterator operator()(typename basic_format_arg::handle handle) { - handle.format(ctx_); + handle.format(get_parse_context(ctx_), ctx_); return this->out(); } }; @@ -3181,27 +3182,30 @@ struct format_handler : internal::error_handler { void on_arg_id() { arg = context.next_arg(); } void on_arg_id(unsigned id) { - context.parse_context().check_arg_id(id); + get_parse_context(context).check_arg_id(id); arg = context.arg(id); } void on_arg_id(basic_string_view id) { arg = context.arg(id); } void on_replacement_field(const Char* p) { - context.parse_context().advance_to(p); + get_parse_context(context).advance_to(p); internal::custom_formatter f(context); if (!visit_format_arg(f, arg)) context.advance_to(visit_format_arg(ArgFormatter(context), arg)); } const Char* on_format_specs(const Char* begin, const Char* end) { - auto& parse_ctx = context.parse_context(); + auto& parse_ctx = get_parse_context(context); parse_ctx.advance_to(begin); internal::custom_formatter f(context); if (visit_format_arg(f, arg)) return parse_ctx.begin(); basic_format_specs specs; using internal::specs_handler; - internal::specs_checker> handler( - specs_handler(specs, context), arg.type()); + typedef basic_parse_context parse_context; + internal::specs_checker> handler( + specs_handler(specs, get_parse_context(context), + context), + arg.type()); begin = parse_format_specs(begin, end, handler); if (begin == end || *begin != '}') on_error("missing '}' in format string"); parse_ctx.advance_to(begin); diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 45510d82..d7375db5 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -57,7 +57,7 @@ template class is_streamable { private: template static decltype((void)(internal::declval&>() - << internal::declval()), + << internal::declval()), std::true_type()) test(int); diff --git a/include/fmt/prepare.h b/include/fmt/prepare.h index ab7fdf45..e46a2358 100644 --- a/include/fmt/prepare.h +++ b/include/fmt/prepare.h @@ -310,12 +310,12 @@ class prepared_format { const format_part_t& part) const { const auto view = to_string_view(format_); const auto specification_begin = view.data() + part.end_of_argument_id; - ctx.parse_context().advance_to(specification_begin); + get_parse_context(ctx).advance_to(specification_begin); } template void format_arg(Context& ctx, Id arg_id) const { - ctx.parse_context().check_arg_id(arg_id); + get_parse_context(ctx).check_arg_id(arg_id); const auto stopped_at = visit_format_arg(arg_formatter(ctx), ctx.arg(arg_id)); ctx.advance_to(stopped_at); @@ -537,9 +537,9 @@ struct parts_container_concept_check : std::true_type { template static std::false_type has_add_check(check_second); template - static decltype((void)declval().add( - declval()), - std::true_type()) has_add_check(check_first); + static decltype( + (void)declval().add(declval()), + std::true_type()) has_add_check(check_first); typedef decltype(has_add_check(check_first())) has_add; static_assert(has_add::value, "PartsContainer doesn't provide add() method"); @@ -554,10 +554,9 @@ struct parts_container_concept_check : std::true_type { template static std::false_type has_substitute_last_check(check_second); template - static decltype( - (void)declval().substitute_last( - declval()), - std::true_type()) has_substitute_last_check(check_first); + static decltype((void)declval().substitute_last( + declval()), + std::true_type()) has_substitute_last_check(check_first); typedef decltype(has_substitute_last_check( check_first())) has_substitute_last; static_assert(has_substitute_last::value, diff --git a/include/fmt/printf.h b/include/fmt/printf.h index cd897467..2a873311 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -319,7 +319,7 @@ class printf_arg_formatter /** Formats an argument of a custom (user-defined) type. */ iterator operator()(typename basic_format_arg::handle handle) { - handle.format(context_); + handle.format(get_parse_context(context_), context_); return this->out(); } }; @@ -380,7 +380,6 @@ class basic_printf_context : using base::advance_to; using base::out; - using base::parse_context; /** Formats stored arguments and writes the output to the range. */ OutputIt format(); @@ -417,7 +416,7 @@ template typename basic_printf_context::format_arg basic_printf_context::get_arg(unsigned arg_index) { if (arg_index == std::numeric_limits::max()) - return this->do_get_arg(this->parse_context().next_arg_id()); + return this->do_get_arg(get_parse_context(*this).next_arg_id()); return base::arg(arg_index - 1); } @@ -462,7 +461,7 @@ unsigned basic_printf_context::parse_header( template OutputIt basic_printf_context::format() { auto out = this->out(); - const auto range = this->parse_context(); + const auto range = get_parse_context(*this); const Char* const end = range.end(); const Char* start = range.begin(); auto it = start; diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 9e60de4b..5145fc06 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -104,11 +104,10 @@ struct is_range_< /// tuple_size and tuple_element check. template class is_tuple_like_ { template - static auto check(U* p) - -> decltype(std::tuple_size::value, - (void)internal::declval< - typename std::tuple_element<0, U>::type>(), - int()); + static auto check(U* p) -> decltype( + std::tuple_size::value, + (void)internal::declval::type>(), + int()); template static void check(...); public: diff --git a/include/format b/include/format index 20b4b815..bdca7bdb 100644 --- a/include/format +++ b/include/format @@ -582,8 +582,9 @@ struct format_handler : fmt::internal::error_handler { if (visit_format_arg(f, arg)) return parse_ctx.begin(); fmt::basic_format_specs specs; using fmt::internal::specs_handler; - fmt::internal::specs_checker> handler( - specs_handler(specs, context), get_type(arg)); + using parse_context = basic_format_parse_context; + fmt::internal::specs_checker> handler( + specs_handler(specs, parse_ctx, context), get_type(arg)); begin = parse_format_specs(begin, end, handler); if (begin == end || *begin != '}') on_error("missing '}' in format string"); parse_ctx.advance_to(begin); diff --git a/test/core-test.cc b/test/core-test.cc index 883a7983..067f1353 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -223,10 +223,9 @@ struct custom_context { }; bool called; + fmt::format_parse_context ctx; - fmt::format_parse_context parse_context() { - return fmt::format_parse_context(""); - } + fmt::format_parse_context& parse_context() { return ctx; } void advance_to(const char*) {} }; @@ -234,8 +233,8 @@ TEST(ArgTest, MakeValueWithCustomContext) { test_struct t; fmt::internal::value arg = fmt::internal::make_value(t); - custom_context ctx = {false}; - arg.custom.format(&t, ctx); + custom_context ctx = {false, fmt::format_parse_context("")}; + arg.custom.format(&t, ctx.parse_context(), ctx); EXPECT_TRUE(ctx.called); } @@ -379,7 +378,7 @@ struct check_custom { } buffer; fmt::internal::basic_buffer& base = buffer; fmt::format_context ctx(std::back_inserter(base), "", fmt::format_args()); - h.format(ctx); + h.format(get_parse_context(ctx), ctx); EXPECT_EQ("test", std::string(buffer.data, buffer.size())); return test_result(); } diff --git a/test/format-test.cc b/test/format-test.cc index 5db36526..78c98679 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2164,6 +2164,18 @@ TEST(FormatTest, ConstexprParseFormatSpecs) { static_assert(parse_test_specs("{<").res == handler::ERROR, ""); } +struct test_parse_context { + typedef char char_type; + + FMT_CONSTEXPR unsigned next_arg_id() { return 33; } + template FMT_CONSTEXPR void check_arg_id(Id) {} + + FMT_CONSTEXPR const char* begin() { return FMT_NULL; } + FMT_CONSTEXPR const char* end() { return FMT_NULL; } + + void on_error(const char*) {} +}; + struct test_context { typedef char char_type; typedef fmt::basic_format_arg format_arg; @@ -2181,23 +2193,18 @@ struct test_context { return fmt::internal::make_arg(22); } - template FMT_CONSTEXPR void check_arg_id(Id) {} - - FMT_CONSTEXPR unsigned next_arg_id() { return 33; } - void on_error(const char*) {} - FMT_CONSTEXPR test_context& parse_context() { return *this; } FMT_CONSTEXPR test_context error_handler() { return *this; } - FMT_CONSTEXPR const char* begin() { return FMT_NULL; } - FMT_CONSTEXPR const char* end() { return FMT_NULL; } }; template FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) { fmt::format_specs specs; + test_parse_context parse_ctx; test_context ctx{}; - fmt::internal::specs_handler h(specs, ctx); + fmt::internal::specs_handler h( + specs, parse_ctx, ctx); parse_format_specs(s, s + N, h); return specs; } @@ -2223,8 +2230,8 @@ template FMT_CONSTEXPR fmt::internal::dynamic_format_specs parse_dynamic_specs( const char (&s)[N]) { fmt::internal::dynamic_format_specs specs; - test_context ctx{}; - fmt::internal::dynamic_specs_handler h(specs, ctx); + test_parse_context ctx{}; + fmt::internal::dynamic_specs_handler h(specs, ctx); parse_format_specs(s, s + N, h); return specs; }