diff --git a/include/fmt/base.h b/include/fmt/base.h index 2ea13bf5..efda6db5 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -2308,8 +2308,7 @@ template class value { // 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`. - custom.format = format_custom_arg< - value_type, typename Context::template formatter_type>; + custom.format = format_custom>; } FMT_ALWAYS_INLINE value(const named_arg_info* args, size_t size) : named_args{args, size} {} @@ -2320,9 +2319,8 @@ template class value { private: // Formats an argument of a custom type, such as a user-defined class. template - static void format_custom_arg(void* arg, - typename Context::parse_context_type& parse_ctx, - Context& ctx) { + static void format_custom(void* arg, parse_context& parse_ctx, + Context& ctx) { auto f = Formatter(); parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = @@ -2494,7 +2492,67 @@ struct format_arg_store { arg_t args[NUM_ARGS != 0 ? NUM_ARGS : +1]; }; +// TYPE can be different from type_constant, e.g. for __float128. +template struct native_formatter { + private: + dynamic_format_specs specs_; + + public: + using nonlocking = void; + + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); + if (const_check(TYPE == type::char_type)) check_char_specs(specs_); + return end; + } + + template + FMT_CONSTEXPR void set_debug_format(bool set = true) { + specs_.set_type(set ? presentation_type::debug : presentation_type::none); + } + + template + FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const + -> decltype(ctx.out()); +}; + +template +struct locking + : bool_constant::value == type::custom_type> {}; +template +struct locking>::nonlocking>> + : std::false_type {}; + +template FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value; +} +template +FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value || is_locking(); +} + +// Use vformat_args and avoid type_identity to keep symbols short. +template struct vformat_args { + using type = basic_format_args>; +}; +template <> struct vformat_args { + using type = format_args; +}; + +template +void vformat_to(buffer& buf, basic_string_view fmt, + typename vformat_args::type args, locale_ref loc = {}); + +#ifdef _WIN32 +FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool); +#else +inline void vprint_mojibake(FILE*, string_view, const format_args&, bool) {} +#endif } // namespace detail + FMT_BEGIN_EXPORT // A formatting argument. Context is a template parameter for the compiled API @@ -2522,16 +2580,15 @@ template class basic_format_arg { public: class handle { + private: + detail::custom_value custom_; + public: explicit handle(detail::custom_value custom) : custom_(custom) {} - void format(typename Context::parse_context_type& parse_ctx, - Context& ctx) const { + void format(parse_context& parse_ctx, Context& ctx) const { custom_.format(custom_.value, parse_ctx, ctx); } - - private: - detail::custom_value custom_; }; constexpr basic_format_arg() : type_(detail::type::none_type) {} @@ -2582,7 +2639,7 @@ template class basic_format_arg { case detail::type::cstring_type: return vis(value_.string.data); case detail::type::string_type: - using sv = basic_string_view; + using sv = basic_string_view; return vis(sv(value_.string.data, value_.string.size)); case detail::type::pointer_type: return vis(value_.pointer); @@ -2728,7 +2785,7 @@ class context { using iterator = appender; using format_arg = basic_format_arg; using parse_context_type = parse_context; - template using formatter_type = formatter; + template using formatter_type FMT_DEPRECATED = formatter; enum { builtin_types = FMT_BUILTIN_TYPES }; /// Constructs a `context` object. References to the arguments are stored @@ -2755,71 +2812,6 @@ class context { FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } }; -FMT_END_EXPORT - -namespace detail { - -// TYPE can be different from type_constant, e.g. for __float128. -template struct native_formatter { - private: - dynamic_format_specs specs_; - - public: - using nonlocking = void; - - FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { - if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); - auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, TYPE); - if (const_check(TYPE == type::char_type)) check_char_specs(specs_); - return end; - } - - template - FMT_CONSTEXPR void set_debug_format(bool set = true) { - specs_.set_type(set ? presentation_type::debug : presentation_type::none); - } - - template - FMT_CONSTEXPR auto format(const T& val, FormatContext& ctx) const - -> decltype(ctx.out()); -}; - -template -struct locking - : bool_constant::value == type::custom_type> {}; -template -struct locking>::nonlocking>> - : std::false_type {}; - -template FMT_CONSTEXPR inline auto is_locking() -> bool { - return locking::value; -} -template -FMT_CONSTEXPR inline auto is_locking() -> bool { - return locking::value || is_locking(); -} - -// Use vformat_args and avoid type_identity to keep symbols short. -template struct vformat_args { - using type = basic_format_args>; -}; -template <> struct vformat_args { - using type = format_args; -}; - -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}); - -FMT_API void vprint_mojibake(FILE*, string_view, format_args, bool = false); -#ifndef _WIN32 -inline void vprint_mojibake(FILE*, string_view, format_args, bool) {} -#endif -} // namespace detail - -FMT_BEGIN_EXPORT template struct runtime_format_string { basic_string_view str; @@ -3060,7 +3052,7 @@ FMT_API void vprintln(FILE* f, string_view fmt, format_args args); template FMT_INLINE void print(format_string fmt, T&&... args) { const auto& vargs = fmt::make_format_args(args...); - if (!FMT_USE_UTF8) return detail::vprint_mojibake(stdout, fmt, vargs); + if (!FMT_USE_UTF8) return detail::vprint_mojibake(stdout, fmt, vargs, false); return detail::is_locking() ? vprint_buffered(stdout, fmt, vargs) : vprint(fmt, vargs); } @@ -3076,7 +3068,7 @@ FMT_INLINE void print(format_string fmt, T&&... args) { template FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { const auto& vargs = fmt::make_format_args(args...); - if (!FMT_USE_UTF8) return detail::vprint_mojibake(f, fmt, vargs); + if (!FMT_USE_UTF8) return detail::vprint_mojibake(f, fmt, vargs, false); return detail::is_locking() ? vprint_buffered(f, fmt, vargs) : vprint(f, fmt, vargs); } diff --git a/test/base-test.cc b/test/base-test.cc index c5ba3a80..5c3f91ca 100644 --- a/test/base-test.cc +++ b/test/base-test.cc @@ -276,27 +276,6 @@ TEST(base_test, get_buffer) { EXPECT_EQ(&back_inserter_result, buffer_ptr); } -struct custom_context { - using char_type = char; - using parse_context_type = fmt::format_parse_context; - - bool called = false; - - template struct formatter_type { - FMT_CONSTEXPR auto parse(fmt::format_parse_context& ctx) - -> decltype(ctx.begin()) { - return ctx.begin(); - } - - const char* format(const T&, custom_context& ctx) const { - ctx.called = true; - return nullptr; - } - }; - - void advance_to(const char*) {} -}; - struct test_struct {}; FMT_BEGIN_NAMESPACE @@ -316,16 +295,6 @@ TEST(arg_test, format_args) { EXPECT_FALSE(args.get(1)); } -TEST(arg_test, make_value_with_custom_context) { - auto t = test_struct(); - auto arg = fmt::detail::value( - fmt::detail::arg_mapper().map(t)); - auto ctx = custom_context(); - auto parse_ctx = fmt::format_parse_context(""); - arg.custom.format(&t, parse_ctx, ctx); - EXPECT_TRUE(ctx.called); -} - // Use a unique result type to make sure that there are no undesirable // conversions. struct test_result {};