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();