diff --git a/fmt/format.h b/fmt/format.h index c5de6921..db8e79ce 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -267,6 +267,14 @@ typedef __int64 intmax_t; (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) #endif +// Checks if decltype v1.1 is supported +// http://en.cppreference.com/w/cpp/compiler_support +#define FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES \ + (FMT_HAS_FEATURE(cxx_decltype_incomplete_return_types) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ + FMT_MSC_VER >= 1900 || \ + FMT_ICC_VERSION >= 1200) + #ifdef FMT_HEADER_ONLY // If header only do not use extern templates. # undef FMT_USE_EXTERN_TEMPLATES diff --git a/fmt/ostream.h b/fmt/ostream.h index 4e8c6d8c..2d2df2c2 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,20 @@ struct ConvertToIntImpl { // Write the content of w to os. void write(std::ostream &os, Writer &w); + +#if FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES +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; +}; +#endif } // namespace internal // Formats a value. @@ -97,6 +115,30 @@ void format_arg(BasicFormatter &f, */ FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_VARIADIC(void, print, std::ostream &, CStringRef) + +#if __cplusplus >= 201103L +template +typename std::enable_if< + !std::is_same< + typename std::remove_cv::type>::type, + char * + >::value, + BasicWriter& +>::type +operator<<(BasicWriter &writer, const T &value) { + FMT_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; +} +#endif } // namespace fmt #ifdef FMT_HEADER_ONLY diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 4081b43f..a7341065 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -111,6 +111,14 @@ std::ostream &operator<<(std::ostream &os, EmptyTest) { return os << ""; } +#if __cplusplus >= 201103L +struct UserDefinedTest { int i = 42; }; + +std::ostream &operator<<(std::ostream &os, const UserDefinedTest &u) { + return os << u.i; +} +#endif + TEST(OStreamTest, EmptyCustomOutput) { EXPECT_EQ("", fmt::format("{}", EmptyTest())); } @@ -129,6 +137,17 @@ TEST(OStreamTest, WriteToOStream) { EXPECT_EQ("foo", os.str()); } +#if __cplusplus >= 201103L +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()); +} +#endif + TEST(OStreamTest, WriteToOStreamMaxSize) { std::size_t max_size = std::numeric_limits::max(); std::streamsize max_streamsize = std::numeric_limits::max();