diff --git a/include/fmt/core.h b/include/fmt/core.h index 3bbbcce4110d3..5ec24cd76d28f 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -498,11 +498,25 @@ template class basic_format_args; // A formatter for objects of type T. template struct formatter { + formatter() = delete; +}; + +template +struct convert_to_int + : std::integral_constant::value && + std::is_convertible::value> {}; + +namespace internal { + +template +struct fallback_formatter { static_assert( internal::no_formatter_error::value, "don't know how to format the type, include fmt/ostream.h if it provides " "an operator<< that should be used"); + fallback_formatter() = delete; + // The following functions are not defined intentionally. template typename ParseContext::iterator parse(ParseContext&); @@ -510,13 +524,6 @@ struct formatter { auto format(const T& val, FormatContext& ctx) -> decltype(ctx.out()); }; -template -struct convert_to_int - : std::integral_constant::value && - std::is_convertible::value> {}; - -namespace internal { - struct dummy_string_view { typedef void char_type; }; @@ -623,9 +630,29 @@ template class value { } value(const void* val) { pointer = val; } - template explicit value(const T& val) { + template ::type>{}, + int>::type = 0> + explicit value(const T& val) { custom.value = &val; - custom.format = &format_custom_arg; + // 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`. + typedef typename Context::template formatter_type::type formatter; + custom.format = &format_custom_arg; + } + + template ::type>{}, + int>::type = 0> + explicit value(const T& val) { + custom.value = &val; + custom.format = + &format_custom_arg>; } const named_arg_base& as_named_arg() { @@ -634,12 +661,9 @@ template class value { private: // Formats an argument of a custom type, such as a user-defined class. - template + template static void format_custom_arg(const void* arg, Context& ctx) { - // 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::type f; + 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)); diff --git a/include/fmt/format.h b/include/fmt/format.h index e04391e42f434..3b84976a2bdeb 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1702,8 +1702,7 @@ 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) { + template FMT_CONSTEXPR format_arg get_arg(Id arg_id) { context_.parse_context().check_arg_id(arg_id); return context_.arg(arg_id); } diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 8ae1e9a43e7d6..d136a0e23ac2b 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -93,22 +93,12 @@ void format_value(basic_buffer& buffer, const T& value) { output << value; buffer.resize(buffer.size()); } -} // namespace internal - -// Disable conversion to int if T has an overloaded operator<< which is a free -// function (not a member of std::ostream). -template struct convert_to_int { - static const bool value = convert_to_int::value && - !internal::is_streamable::value; -}; // Formats an object of type T that has an overloaded ostream operator<<. template -struct formatter::value && - !internal::format_type::type, - T>::value>::type> +struct fallback_formatter< + T, Char, + typename std::enable_if::value>::type> : formatter, Char> { template auto format(const T& value, Context& ctx) -> decltype(ctx.out()) { @@ -118,6 +108,14 @@ struct formatter, Char>::format(str, ctx); } }; +} // namespace internal + +// Disable conversion to int if T has an overloaded operator<< which is a free +// function (not a member of std::ostream). +template struct convert_to_int { + static const bool value = convert_to_int::value && + !internal::is_streamable::value; +}; template inline void vprint( diff --git a/test/format-test.cc b/test/format-test.cc index 8c1977c874c1b..14022468fe431 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -2144,6 +2144,10 @@ struct test_context { typedef char char_type; typedef fmt::basic_format_arg format_arg; + template struct formatter_type { + typedef fmt::formatter type; + }; + FMT_CONSTEXPR fmt::basic_format_arg next_arg() { return fmt::internal::make_arg(11); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 860a621146698..bf40fc1cd4bf1 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -46,6 +46,26 @@ TEST(OStreamTest, Enum) { EXPECT_EQ(L"0", fmt::format(L"{}", A)); } +template +struct TestTemplate {}; + +template +std::ostream& operator<<(std::ostream& os, TestTemplate) { + return os << 1; +} + +template +struct fmt::formatter> : fmt::formatter { + template + typename FormatContext::iterator format(TestTemplate, FormatContext& ctx) { + return formatter::format(2, ctx); + } +}; + +TEST(OStreamTest, Template) { + EXPECT_EQ("2", fmt::format("{}", TestTemplate())); +} + typedef fmt::back_insert_range range; struct test_arg_formatter : fmt::arg_formatter {