Commit 67662702 authored by Andreone's avatar Andreone Committed by Jonathan Müller

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
parent 9e9ad57f
...@@ -267,6 +267,14 @@ typedef __int64 intmax_t; ...@@ -267,6 +267,14 @@ typedef __int64 intmax_t;
(FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11)) (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11))
#endif #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 #ifdef FMT_HEADER_ONLY
// If header only do not use extern templates. // If header only do not use extern templates.
# undef FMT_USE_EXTERN_TEMPLATES # undef FMT_USE_EXTERN_TEMPLATES
......
...@@ -31,6 +31,10 @@ class FormatBuf : public std::basic_streambuf<Char> { ...@@ -31,6 +31,10 @@ class FormatBuf : public std::basic_streambuf<Char> {
this->setp(start_, start_ + buffer_.capacity()); 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()) { int_type overflow(int_type ch = traits_type::eof()) {
if (!traits_type::eq_int_type(ch, traits_type::eof())) { if (!traits_type::eq_int_type(ch, traits_type::eof())) {
size_t buf_size = size(); size_t buf_size = size();
...@@ -69,6 +73,20 @@ struct ConvertToIntImpl<T, true> { ...@@ -69,6 +73,20 @@ struct ConvertToIntImpl<T, true> {
// Write the content of w to os. // Write the content of w to os.
void write(std::ostream &os, Writer &w); 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 } // namespace internal
// Formats a value. // Formats a value.
...@@ -97,6 +115,30 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f, ...@@ -97,6 +115,30 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f,
*/ */
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef) 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 } // namespace fmt
#ifdef FMT_HEADER_ONLY #ifdef FMT_HEADER_ONLY
......
...@@ -111,6 +111,14 @@ std::ostream &operator<<(std::ostream &os, EmptyTest) { ...@@ -111,6 +111,14 @@ std::ostream &operator<<(std::ostream &os, EmptyTest) {
return os << ""; 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) { TEST(OStreamTest, EmptyCustomOutput) {
EXPECT_EQ("", fmt::format("{}", EmptyTest())); EXPECT_EQ("", fmt::format("{}", EmptyTest()));
} }
...@@ -129,6 +137,17 @@ TEST(OStreamTest, WriteToOStream) { ...@@ -129,6 +137,17 @@ TEST(OStreamTest, WriteToOStream) {
EXPECT_EQ("foo", os.str()); 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) { TEST(OStreamTest, WriteToOStreamMaxSize) {
std::size_t max_size = std::numeric_limits<std::size_t>::max(); std::size_t max_size = std::numeric_limits<std::size_t>::max();
std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max(); std::streamsize max_streamsize = std::numeric_limits<std::streamsize>::max();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment