From 56815638981f56982c209da90e1ecda09f3011ec Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Fri, 3 Sep 2021 09:33:24 -0700 Subject: [PATCH] Cleanup ostream interface --- include/fmt/ostream.h | 70 ++++++++++++++++++------------------------- test/ostream-test.cc | 3 +- 2 files changed, 31 insertions(+), 42 deletions(-) diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index cee8273b..d6769880 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -30,56 +30,35 @@ template class formatbuf : public std::basic_streambuf { formatbuf(buffer& buf) : buffer_(buf) {} protected: - // The put-area is actually always empty. This makes the implementation - // simpler and has the advantage that the streambuf and the buffer are always - // in sync and sputc never writes into uninitialized memory. The obvious - // disadvantage is that each call to sputc always results in a (virtual) call - // to overflow. There is no disadvantage here for sputn since this always - // results in a call to xsputn. + // The put area is always empty. This makes the implementation simpler and has + // the advantage that the streambuf and the buffer are always in sync and + // sputc never writes into uninitialized memory. A disadvantage is that each + // call to sputc always results in a (virtual) call to overflow. There is no + // disadvantage here for sputn since this always results in a call to xsputn. - int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { + auto overflow(int_type ch = traits_type::eof()) -> int_type FMT_OVERRIDE { if (!traits_type::eq_int_type(ch, traits_type::eof())) buffer_.push_back(static_cast(ch)); return ch; } - std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE { + auto xsputn(const Char* s, std::streamsize count) + -> std::streamsize override { buffer_.append(s, s + count); return count; } }; -struct converter { - template ::value)> converter(T); -}; - -template struct test_stream : std::basic_ostream { - private: - void_t<> operator<<(converter); -}; - -// Hide insertion operators for built-in types. -template -void_t<> operator<<(std::basic_ostream&, Char); -template -void_t<> operator<<(std::basic_ostream&, char); -template -void_t<> operator<<(std::basic_ostream&, char); -template -void_t<> operator<<(std::basic_ostream&, signed char); -template -void_t<> operator<<(std::basic_ostream&, unsigned char); - -// Checks if T has a user-defined operator<< e.g. not a member of std::ostream. -template class is_streamable { +// Checks if T has a user-defined operator<<. +template +class is_streamable { private: template - static bool_constant&>() - << std::declval()), - void_t<>>::value> - test(int); + static auto test(int) + -> bool_constant&>() + << std::declval()) != 0>; - template static std::false_type test(...); + template static auto test(...) -> std::false_type; using result = decltype(test(0)); @@ -89,10 +68,15 @@ template class is_streamable { static const bool value = result::value; }; -// Formatting of arrays is intentionally disabled to prevent conflicts with -// standard (non-ostream) formatters. -template -struct is_streamable : std::false_type {}; +// Formatting of built-in types and arrays is intentionally disabled because +// it's handled by standard (non-ostream) formatters. +template +struct is_streamable< + T, Char, + enable_if_t::value || std::is_array::value || + std::is_same::value || + (std::is_convertible::value && + !std::is_enum::value)>> : std::false_type {}; // Write the content of buf to os. template @@ -130,6 +114,8 @@ struct fallback_formatter::value>> -> decltype(ctx.begin()) { return formatter, Char>::parse(ctx); } + + // DEPRECATED! template >::value)> @@ -145,6 +131,8 @@ struct fallback_formatter::value>> basic_string_view str(buffer.data(), buffer.size()); return formatter, Char>::format(str, ctx); } + + // DEPRECATED! template auto format(const T& value, basic_printf_context& ctx) -> OutputIt { @@ -159,7 +147,7 @@ FMT_MODULE_EXPORT template void vprint(std::basic_ostream& os, basic_string_view format_str, basic_format_args>> args) { - basic_memory_buffer buffer; + auto buffer = basic_memory_buffer(); detail::vformat_to(buffer, format_str, args); detail::write_buffer(os, buffer); } diff --git a/test/ostream-test.cc b/test/ostream-test.cc index b0dd3c4b..2798db7e 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -258,7 +258,8 @@ std::ostream& operator<<(std::ostream& os, streamable_and_convertible_to_bool) { } TEST(ostream_test, format_convertible_to_bool) { - EXPECT_EQ("foo", fmt::format("{}", streamable_and_convertible_to_bool())); + // operator<< is intentionally not used because of potential ODR violations. + EXPECT_EQ(fmt::format("{}", streamable_and_convertible_to_bool()), "true"); } struct copyfmt_test {};