From 28f0d56f378ecfb78f2d184cad0f49def9f0dd8b Mon Sep 17 00:00:00 2001 From: Nicolas Fauvet Date: Tue, 17 Jan 2017 16:03:00 +0100 Subject: [PATCH] allow to stream user defined types in a MemoryWriter --- fmt/ostream.h | 40 ++++++++++++++++++++++++++++++++++++++++ test/ostream-test.cc | 14 ++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/fmt/ostream.h b/fmt/ostream.h index 4e8c6d8c..dff0b9fe 100644 --- a/fmt/ostream.h +++ b/fmt/ostream.h @@ -31,6 +31,10 @@ class FormatBuf : public std::basic_streambuf { this->setp(start_, start_ + buffer_.capacity()); } + FormatBuf(Buffer &buffer, Char *start) : buffer_(buffer) , start_(start) { + this->setp(start_, start_ + buffer_.capacity()); + } + int_type overflow(int_type ch = traits_type::eof()) { if (!traits_type::eq_int_type(ch, traits_type::eof())) { size_t buf_size = size(); @@ -69,6 +73,19 @@ struct ConvertToIntImpl { // Write the content of w to os. void write(std::ostream &os, Writer &w); + +template +class is_streamable +{ + template + static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...) -> std::false_type; + +public: + static constexpr bool value = decltype(test(0))::value; +}; } // namespace internal // Formats a value. @@ -97,6 +114,29 @@ void format_arg(BasicFormatter &f, */ FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_VARIADIC(void, print, std::ostream &, CStringRef) + +template +typename std::enable_if< + !std::is_same< + typename std::remove_cv::type>::type, + char * + >::value, + BasicWriter& +>::type +operator<<(BasicWriter &writer, const T &value) +{ + static_assert(internal::is_streamable::value, "T must be Streamable"); + + auto &buffer = writer.buffer(); + Char *start = &buffer[0] + buffer.size(); + + internal::FormatBuf format_buf(buffer, start); + std::basic_ostream output(&format_buf); + output << value; + + buffer.resize(buffer.size() + format_buf.size()); + return writer; +} } // namespace fmt #ifdef FMT_HEADER_ONLY diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 4081b43f..8f4f6a2c 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -111,6 +111,11 @@ std::ostream &operator<<(std::ostream &os, EmptyTest) { return os << ""; } +struct UserDefinedTest { int i = 42; }; +std::ostream &operator<<(std::ostream &os, const UserDefinedTest &u) { + return os << u.i; +} + TEST(OStreamTest, EmptyCustomOutput) { EXPECT_EQ("", fmt::format("{}", EmptyTest())); } @@ -129,6 +134,15 @@ TEST(OStreamTest, WriteToOStream) { EXPECT_EQ("foo", os.str()); } +TEST(OStreamTest, WriteUserDefinedTypeToOStream) { + std::ostringstream os; + fmt::MemoryWriter w; + UserDefinedTest u; + w << "The answer is " << u; + fmt::internal::write(os, w); + EXPECT_EQ("The answer is 42", os.str()); +} + TEST(OStreamTest, WriteToOStreamMaxSize) { std::size_t max_size = std::numeric_limits::max(); std::streamsize max_streamsize = std::numeric_limits::max();