allow to stream user defined types in a MemoryWriter (#456)

* allow to stream user defined types in a MemoryWriter

* fix indent

* follow Google C++ Style

* make code c++98 compatible

* fix macro usage

* disable ability to stream user defined types if not at least c++11

* fix for disable ability to stream user defined types if not at least c++11

* use FMT_STATIC_ASSERT
diff --git a/fmt/format.h b/fmt/format.h
index c5de692..db8e79c 100644
--- a/fmt/format.h
+++ b/fmt/format.h
@@ -267,6 +267,14 @@
      (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 4e8c6d8..2d2df2c 100644
--- a/fmt/ostream.h
+++ b/fmt/ostream.h
@@ -31,6 +31,10 @@
     this->setp(start_, start_ + buffer_.capacity());
   }
 
+  FormatBuf(Buffer<Char> &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 @@
 
 // Write the content of w to os.
 void write(std::ostream &os, Writer &w);
+
+#if FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES
+template<typename T>
+class is_streamable {
+  template<typename U>
+  static auto test(int) -> decltype(std::declval<std::ostream &>() << std::declval<U>(), std::true_type());
+
+  template<typename>
+  static auto test(...) -> std::false_type;
+
+public:
+  static constexpr bool value = decltype(test<T>(0))::value;
+};
+#endif
 }  // namespace internal
 
 // Formats a value.
@@ -97,6 +115,30 @@
  */
 FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
 FMT_VARIADIC(void, print, std::ostream &, CStringRef)
+
+#if __cplusplus >= 201103L
+template<typename T, typename Char>
+typename std::enable_if<
+ !std::is_same<
+   typename std::remove_cv<typename std::decay<T>::type>::type,
+   char *
+ >::value,
+ BasicWriter<Char>&
+>::type
+operator<<(BasicWriter<Char> &writer, const T &value) {
+  FMT_STATIC_ASSERT(internal::is_streamable<T>::value, "T must be Streamable");
+
+  auto &buffer = writer.buffer();
+  Char *start = &buffer[0] + buffer.size();
+
+  internal::FormatBuf<Char> format_buf(buffer, start);
+  std::basic_ostream<Char> 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 4081b43..a734106 100644
--- a/test/ostream-test.cc
+++ b/test/ostream-test.cc
@@ -111,6 +111,14 @@
   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 @@
   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<std::size_t>::max();
   std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();