From 01c51ffb479f5236083e6faf98be9cfa7489dcc4 Mon Sep 17 00:00:00 2001 From: effzeh Date: Tue, 11 Apr 2017 20:25:02 +0200 Subject: [PATCH 1/5] Consistent behavior of operator<< for known types --- fmt/format.cc | 2 +- fmt/format.h | 382 ++++++++++++++++++++++++++++++++----------- fmt/ostream.h | 16 +- test/format-test.cc | 206 +++++++++++++++++------ test/ostream-test.cc | 19 +-- 5 files changed, 461 insertions(+), 164 deletions(-) diff --git a/fmt/format.cc b/fmt/format.cc index 494a4a71..c7b4fc00 100644 --- a/fmt/format.cc +++ b/fmt/format.cc @@ -368,7 +368,7 @@ FMT_FUNC void internal::format_windows_error( if (result != 0) { UTF16ToUTF8 utf8_message; if (utf8_message.convert(system_message) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; + out << message << ": " << StringRef(utf8_message); return; } break; diff --git a/fmt/format.h b/fmt/format.h index 07d02098..9803a27d 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -155,6 +155,24 @@ typedef __int64 intmax_t; # endif #endif +#ifndef FMT_USE_OSTREAM_RVALUE +// If non-zero, enables operator<< for BasicWriter&&, such that things like +// std::string s = (MemoryWriter() << "i = " << i).str(); +// are possible. +// +// (See: http://cplusplus.github.io/LWG/lwg-active.html#1203) +// +// XXX: +// I don't know if FMT_USE_RVALUE_REFERENCES is the correct way to enable +// this... It works for g++ 6.3 and VC15, though. +// +# if FMT_USE_RVALUE_REFERENCES +# define FMT_USE_OSTREAM_RVALUE 1 +# else +# define FMT_USE_OSTREAM_RVALUE 0 +# endif +#endif + // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 @@ -2442,6 +2460,8 @@ class SystemError : public internal::RuntimeError { FMT_API void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; +class BasicWriterBase {}; + /** \rst This template provides operations for formatting and writing data into @@ -2461,7 +2481,7 @@ FMT_API void format_system_error(fmt::Writer &out, int error_code, \endrst */ template -class BasicWriter { +class BasicWriter : public BasicWriterBase { private: // Output buffer. Buffer &buffer_; @@ -2541,14 +2561,6 @@ class BasicWriter { void write_str(const internal::Arg::StringValue &str, const Spec &spec); - // This following methods are private to disallow writing wide characters - // and strings to a char stream. If you want to print a wide string as a - // pointer as std::ostream does, cast it to const void*. - // Do not implement! - void operator<<(typename internal::WCharHelper::Unsupported); - void operator<<( - typename internal::WCharHelper::Unsupported); - // Appends floating-point length specifier to the format string. // The second argument is only used for overload resolution. void append_float_length(Char *&format_ptr, long double) { @@ -2578,6 +2590,10 @@ class BasicWriter { */ virtual ~BasicWriter() {} + void clear() FMT_NOEXCEPT { buffer_.clear(); } + + Buffer &buffer() FMT_NOEXCEPT { return buffer_; } + /** Returns the total number of characters written. */ @@ -2639,101 +2655,283 @@ class BasicWriter { } FMT_VARIADIC_VOID(write, BasicCStringRef) - BasicWriter &operator<<(int value) { + template + void append_decimal(Int value) { write_decimal(value); - return *this; - } - BasicWriter &operator<<(unsigned value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(long value) { - write_decimal(value); - return *this; - } - BasicWriter &operator<<(unsigned long value) { - return *this << IntFormatSpec(value); - } - BasicWriter &operator<<(LongLong value) { - write_decimal(value); - return *this; } - /** - \rst - Formats *value* and writes it to the stream. - \endrst - */ - BasicWriter &operator<<(ULongLong value) { - return *this << IntFormatSpec(value); - } - - BasicWriter &operator<<(double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - \rst - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. - \endrst - */ - BasicWriter &operator<<(long double value) { - write_double(value, FormatSpec()); - return *this; - } - - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) { - buffer_.push_back(value); - return *this; - } - - BasicWriter &operator<<( - typename internal::WCharHelper::Supported value) { - buffer_.push_back(value); - return *this; - } - - /** - \rst - Writes *value* to the stream. - \endrst - */ - BasicWriter &operator<<(fmt::BasicStringRef value) { - const Char *str = value.data(); - buffer_.append(str, str + value.size()); - return *this; - } - - BasicWriter &operator<<( - typename internal::WCharHelper::Supported value) { - const char *str = value.data(); - buffer_.append(str, str + value.size()); - return *this; - } - - template - BasicWriter &operator<<(IntFormatSpec spec) { + template + void append_int(const IntFormatSpec &spec) { internal::CharTraits::convert(FillChar()); write_int(spec.value(), spec); - return *this; } - template - BasicWriter &operator<<(const StrFormatSpec &spec) { - const StrChar *s = spec.str(); - write_str(s, std::char_traits::length(s), spec); - return *this; + template // XXX: FloatFormatSpec? + void append_double(Float value) { + write_double(value, FormatSpec()); } - void clear() FMT_NOEXCEPT { buffer_.clear(); } + void append_pointer(const void *p) { + // Note: + // Uses the same format as ArgFormatterBase. + FormatSpec spec; + spec.flags_ = HASH_FLAG; + spec.type_ = 'x'; + write_int(reinterpret_cast(p), spec); + } - Buffer &buffer() FMT_NOEXCEPT { return buffer_; } + void append_char(char ch) { + buffer_.push_back(ch); + } + + void append_char( + typename internal::WCharHelper::Supported ch) { + buffer_.push_back(ch); + } + + void append_str(StringRef str) { + buffer_.append(str.data(), str.data() + str.size()); + } + + void append_str( + typename internal::WCharHelper::Supported str) { + buffer_.append(str.data(), str.data() + str.size()); + } + + void append_str(const StrFormatSpec &spec) { + const char *s = spec.str(); + write_str(s, std::char_traits::length(s), spec); + } + + void append_str( + typename internal::WCharHelper< + const StrFormatSpec &, Char>::Supported spec) { + const wchar_t *s = spec.str(); + write_str(s, std::char_traits::length(s), spec); + } + + private: + // The following methods are private to disallow writing wide characters + // and strings to a char stream. If you want to print a wide string as a + // pointer as std::ostream does, cast it to const void*. + // Do not implement! + void append_char( + typename internal::WCharHelper::Unsupported); + void append_str( + typename internal::WCharHelper::Unsupported); + void append_str( + typename internal::WCharHelper< + const StrFormatSpec &, Char>::Unsupported); }; +template +inline BasicWriter &operator<<(BasicWriter &w, bool value) { + // Note: + // Uses the same format as ArgFormatterBase. + w.append_str(value ? "true" : "false"); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, signed char value) { + w.append_decimal(static_cast(value)); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, short value) { + w.append_decimal(static_cast(value)); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, int value) { + w.append_decimal(static_cast(value)); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, long value) { + w.append_decimal(value); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, LongLong value) { + w.append_decimal(value); + return w; +} + +#if USHRT_MAX <= INT_MAX + +template +inline BasicWriter &operator<<( + BasicWriter &w, unsigned char value) { + w.append_decimal(static_cast(value)); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, unsigned short value) { + w.append_decimal(static_cast(value)); + return w; +} + +#else // !( USHRT_MAX <= INT_MAX ) ----> + +template +inline BasicWriter &operator<<( + BasicWriter &w, unsigned char value) { + w.append_decimal(static_cast(value)); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, unsigned short value) { + w.append_decimal(static_cast(value)); + return w; +} + +#endif + +template +inline BasicWriter &operator<<(BasicWriter &w, unsigned int value) { + w.append_decimal(value); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, unsigned long value) { + w.append_decimal(value); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, ULongLong value) { + w.append_decimal(value); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, const IntFormatSpec &value) { + w.append_int(value); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, float value) { + w.append_double(static_cast(value)); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, double value) { + w.append_double(value); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, long double value) { + w.append_double(value); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, char ch) { + w.append_char(ch); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, wchar_t ch) { + w.append_char(ch); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, const char *value) { + w.append_str(fmt::StringRef(value)); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, const wchar_t *value) { + w.append_str(fmt::WStringRef(value)); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, char *value) { + w.append_str(fmt::StringRef(value)); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, wchar_t *value) { + w.append_str(fmt::WStringRef(value)); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, fmt::BasicStringRef s) { + w.append_str(s); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, + const std::basic_string, Alloc> &value) { + w.append_str(fmt::BasicStringRef(value)); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, const StrFormatSpec &value) { + w.append_str(value); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, const void *pointer) { + w.append_pointer(pointer); + return w; +} + +template +inline BasicWriter &operator<<(BasicWriter &w, void *pointer) { + w.append_pointer(pointer); + return w; +} + +#if FMT_USE_OSTREAM_RVALUE +// http://cplusplus.github.io/LWG/lwg-active.html#1203 +// +// For: +// std::string s = (MemoryWriter() << "i = " << i).str(); +template < + typename WriterT, + typename T, + typename = typename std::enable_if< + !std::is_lvalue_reference::value + && std::is_base_of::value + >::type, + typename = decltype(std::declval() << std::declval()) +> +inline WriterT &&operator<<(WriterT &&w, const T &value) { + w << value; + return std::move(w); +} +#endif + template template typename BasicWriter::CharPtr BasicWriter::write_str( diff --git a/fmt/ostream.h b/fmt/ostream.h index f529e241..9a26b6d3 100644 --- a/fmt/ostream.h +++ b/fmt/ostream.h @@ -111,17 +111,11 @@ 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) { +template +BasicWriter &operator<<(BasicWriter &writer, const T &value) { +#if FMT_HAS_DECLTYPE_INCOMPLETE_RETURN_TYPES FMT_STATIC_ASSERT(internal::is_streamable::value, "T must be Streamable"); +#endif internal::FormatBuf format_buf(writer.buffer()); std::basic_ostream output(&format_buf); @@ -129,7 +123,7 @@ operator<<(BasicWriter &writer, const T &value) { return writer; } -#endif + } // namespace fmt #ifdef FMT_HEADER_ONLY diff --git a/test/format-test.cc b/test/format-test.cc index e0d3ea74..1c672c28 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -79,6 +79,18 @@ using fmt::MemoryWriter; using fmt::WMemoryWriter; using fmt::pad; +namespace fmt { + // Mimic operator<< defined in ostream.h. + // If this gets called in any of the tests below, this is an error. All of the + // types used here should be handled by operator<< defined in format.h or a + // specific overload defined in this file. + template + BasicWriter &operator<<(BasicWriter &w, const T &) { + typename T::this_is_an_error t; + return w; + } +} + namespace { // Format value using the standard library. @@ -107,8 +119,13 @@ void std_format(long double value, std::wstring &result) { // as writing it to std::basic_ostringstream. template ::testing::AssertionResult check_write(const T &value, const char *type) { +#if FMT_USE_OSTREAM_RVALUE std::basic_string actual = (fmt::BasicMemoryWriter() << value).str(); +#else + fmt::BasicMemoryWriter w; + std::basic_string actual = (w << value).str(); +#endif std::basic_string expected; std_format(value, expected); if (expected == actual) @@ -352,25 +369,28 @@ TEST(WriterTest, WriteWideString) { TEST(WriterTest, bin) { using fmt::bin; - EXPECT_EQ("1100101011111110", (MemoryWriter() << bin(0xcafe)).str()); - EXPECT_EQ("1011101010111110", (MemoryWriter() << bin(0xbabeu)).str()); - EXPECT_EQ("1101111010101101", (MemoryWriter() << bin(0xdeadl)).str()); - EXPECT_EQ("1011111011101111", (MemoryWriter() << bin(0xbeeful)).str()); - EXPECT_EQ("11001010111111101011101010111110", - (MemoryWriter() << bin(0xcafebabell)).str()); + MemoryWriter w; + w.clear(); EXPECT_EQ("1100101011111110", (w << bin(0xcafe)).str()); + w.clear(); EXPECT_EQ("1011101010111110", (w << bin(0xbabeu)).str()); + w.clear(); EXPECT_EQ("1101111010101101", (w << bin(0xdeadl)).str()); + w.clear(); EXPECT_EQ("1011111011101111", (w << bin(0xbeeful)).str()); + w.clear(); + EXPECT_EQ("11001010111111101011101010111110", (w << bin(0xcafebabell)).str()); + w.clear(); EXPECT_EQ("11011110101011011011111011101111", - (MemoryWriter() << bin(0xdeadbeefull)).str()); + (w << bin(0xdeadbeefull)).str()); } TEST(WriterTest, oct) { using fmt::oct; - EXPECT_EQ("12", (MemoryWriter() << oct(static_cast(012))).str()); - EXPECT_EQ("12", (MemoryWriter() << oct(012)).str()); - EXPECT_EQ("34", (MemoryWriter() << oct(034u)).str()); - EXPECT_EQ("56", (MemoryWriter() << oct(056l)).str()); - EXPECT_EQ("70", (MemoryWriter() << oct(070ul)).str()); - EXPECT_EQ("1234", (MemoryWriter() << oct(01234ll)).str()); - EXPECT_EQ("5670", (MemoryWriter() << oct(05670ull)).str()); + MemoryWriter w; + w.clear(); EXPECT_EQ("12", (w << oct(static_cast(012))).str()); + w.clear(); EXPECT_EQ("12", (w << oct(012)).str()); + w.clear(); EXPECT_EQ("34", (w << oct(034u)).str()); + w.clear(); EXPECT_EQ("56", (w << oct(056l)).str()); + w.clear(); EXPECT_EQ("70", (w << oct(070ul)).str()); + w.clear(); EXPECT_EQ("1234", (w << oct(01234ll)).str()); + w.clear(); EXPECT_EQ("5670", (w << oct(05670ull)).str()); } TEST(WriterTest, hex) { @@ -380,22 +400,24 @@ TEST(WriterTest, hex) { // This shouldn't compile: //fmt::IntFormatSpec > (*phex2)(short value) = hex; - EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str()); - EXPECT_EQ("babe", (MemoryWriter() << hex(0xbabeu)).str()); - EXPECT_EQ("dead", (MemoryWriter() << hex(0xdeadl)).str()); - EXPECT_EQ("beef", (MemoryWriter() << hex(0xbeeful)).str()); - EXPECT_EQ("cafebabe", (MemoryWriter() << hex(0xcafebabell)).str()); - EXPECT_EQ("deadbeef", (MemoryWriter() << hex(0xdeadbeefull)).str()); + MemoryWriter w; + w.clear(); EXPECT_EQ("cafe", (w << hex(0xcafe)).str()); + w.clear(); EXPECT_EQ("babe", (w << hex(0xbabeu)).str()); + w.clear(); EXPECT_EQ("dead", (w << hex(0xdeadl)).str()); + w.clear(); EXPECT_EQ("beef", (w << hex(0xbeeful)).str()); + w.clear(); EXPECT_EQ("cafebabe", (w << hex(0xcafebabell)).str()); + w.clear(); EXPECT_EQ("deadbeef", (w << hex(0xdeadbeefull)).str()); } TEST(WriterTest, hexu) { using fmt::hexu; - EXPECT_EQ("CAFE", (MemoryWriter() << hexu(0xcafe)).str()); - EXPECT_EQ("BABE", (MemoryWriter() << hexu(0xbabeu)).str()); - EXPECT_EQ("DEAD", (MemoryWriter() << hexu(0xdeadl)).str()); - EXPECT_EQ("BEEF", (MemoryWriter() << hexu(0xbeeful)).str()); - EXPECT_EQ("CAFEBABE", (MemoryWriter() << hexu(0xcafebabell)).str()); - EXPECT_EQ("DEADBEEF", (MemoryWriter() << hexu(0xdeadbeefull)).str()); + MemoryWriter w; + w.clear(); EXPECT_EQ("CAFE", (w << hexu(0xcafe)).str()); + w.clear(); EXPECT_EQ("BABE", (w << hexu(0xbabeu)).str()); + w.clear(); EXPECT_EQ("DEAD", (w << hexu(0xdeadl)).str()); + w.clear(); EXPECT_EQ("BEEF", (w << hexu(0xbeeful)).str()); + w.clear(); EXPECT_EQ("CAFEBABE", (w << hexu(0xcafebabell)).str()); + w.clear(); EXPECT_EQ("DEADBEEF", (w << hexu(0xdeadbeefull)).str()); } template @@ -420,22 +442,22 @@ public: ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); } TEST(WriterTest, pad) { - using fmt::hex; - EXPECT_EQ(" cafe", (MemoryWriter() << pad(hex(0xcafe), 8)).str()); - EXPECT_EQ(" babe", (MemoryWriter() << pad(hex(0xbabeu), 8)).str()); - EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadl), 8)).str()); - EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeeful), 8)).str()); - EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadll), 8)).str()); - EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeefull), 8)).str()); - - EXPECT_EQ(" 11", (MemoryWriter() << pad(11, 7)).str()); - EXPECT_EQ(" 22", (MemoryWriter() << pad(22u, 7)).str()); - EXPECT_EQ(" 33", (MemoryWriter() << pad(33l, 7)).str()); - EXPECT_EQ(" 44", (MemoryWriter() << pad(44ul, 7)).str()); - EXPECT_EQ(" 33", (MemoryWriter() << pad(33ll, 7)).str()); - EXPECT_EQ(" 44", (MemoryWriter() << pad(44ull, 7)).str()); - MemoryWriter w; + using fmt::hex; + w.clear(); EXPECT_EQ(" cafe", (w << pad(hex(0xcafe), 8)).str()); + w.clear(); EXPECT_EQ(" babe", (w << pad(hex(0xbabeu), 8)).str()); + w.clear(); EXPECT_EQ(" dead", (w << pad(hex(0xdeadl), 8)).str()); + w.clear(); EXPECT_EQ(" beef", (w << pad(hex(0xbeeful), 8)).str()); + w.clear(); EXPECT_EQ(" dead", (w << pad(hex(0xdeadll), 8)).str()); + w.clear(); EXPECT_EQ(" beef", (w << pad(hex(0xbeefull), 8)).str()); + + w.clear(); EXPECT_EQ(" 11", (w << pad(11, 7)).str()); + w.clear(); EXPECT_EQ(" 22", (w << pad(22u, 7)).str()); + w.clear(); EXPECT_EQ(" 33", (w << pad(33l, 7)).str()); + w.clear(); EXPECT_EQ(" 44", (w << pad(44ul, 7)).str()); + w.clear(); EXPECT_EQ(" 33", (w << pad(33ll, 7)).str()); + w.clear(); EXPECT_EQ(" 44", (w << pad(44ull, 7)).str()); + w.clear(); w << pad(42, 5, '0'); EXPECT_EQ("00042", w.str()); @@ -448,21 +470,24 @@ TEST(WriterTest, pad) { } TEST(WriterTest, PadString) { - EXPECT_EQ("test ", (MemoryWriter() << pad("test", 8)).str()); - EXPECT_EQ("test******", (MemoryWriter() << pad("test", 10, '*')).str()); + MemoryWriter w; + w.clear(); EXPECT_EQ("test ", (w << pad("test", 8)).str()); + w.clear(); EXPECT_EQ("test******", (w << pad("test", 10, '*')).str()); } TEST(WriterTest, PadWString) { - EXPECT_EQ(L"test ", (WMemoryWriter() << pad(L"test", 8)).str()); - EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, '*')).str()); - EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, L'*')).str()); + WMemoryWriter w; + w.clear(); EXPECT_EQ(L"test ", (w << pad(L"test", 8)).str()); + w.clear(); EXPECT_EQ(L"test******", (w << pad(L"test", 10, '*')).str()); + w.clear(); EXPECT_EQ(L"test******", (w << pad(L"test", 10, L'*')).str()); } TEST(WriterTest, NoConflictWithIOManip) { using namespace std; using namespace fmt; - EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str()); - EXPECT_EQ("12", (MemoryWriter() << oct(012)).str()); + MemoryWriter w; + w.clear(); EXPECT_EQ("cafe", (w << hex(0xcafe)).str()); + w.clear(); EXPECT_EQ("12", (w << oct(012)).str()); } TEST(WriterTest, Format) { @@ -480,7 +505,85 @@ TEST(WriterTest, Format) { } TEST(WriterTest, WWriter) { - EXPECT_EQ(L"cafe", (fmt::WMemoryWriter() << fmt::hex(0xcafe)).str()); + WMemoryWriter w; + EXPECT_EQ(L"cafe", (w << fmt::hex(0xcafe)).str()); +} + +TEST(WriterTest, Stream) { + MemoryWriter w; + + const char *ncs = "ncs"; + + w.clear(); EXPECT_EQ("true", (w << true).str()); + w.clear(); EXPECT_EQ("false", (w << false).str()); + w.clear(); EXPECT_EQ("0", (w << (signed char)0).str()); + w.clear(); EXPECT_EQ("0", (w << (signed short)0).str()); + w.clear(); EXPECT_EQ("0", (w << (signed int)0).str()); + w.clear(); EXPECT_EQ("0", (w << (signed long)0).str()); + w.clear(); EXPECT_EQ("0", (w << (fmt::LongLong)0).str()); + w.clear(); EXPECT_EQ("0", (w << (unsigned char)0).str()); + w.clear(); EXPECT_EQ("0", (w << (unsigned short)0).str()); + w.clear(); EXPECT_EQ("0", (w << (unsigned int)0).str()); + w.clear(); EXPECT_EQ("0", (w << (unsigned long)0).str()); + w.clear(); EXPECT_EQ("0", (w << (fmt::ULongLong)0).str()); + w.clear(); EXPECT_EQ("0", (w << (float)0).str()); + w.clear(); EXPECT_EQ("0", (w << (double)0).str()); + w.clear(); EXPECT_EQ("0", (w << (long double)0).str()); + w.clear(); EXPECT_EQ("0x1234", (w << (const void *)0x1234).str()); + w.clear(); EXPECT_EQ("0x1234", (w << (void *)0x1234).str()); + w.clear(); EXPECT_EQ("x", (w << (char)'x').str()); + w.clear(); EXPECT_EQ("ncs", (w << (const char *)ncs).str()); + w.clear(); EXPECT_EQ("ncs", (w << (char *)ncs).str()); + w.clear(); EXPECT_EQ("ncs", (w << fmt::StringRef(ncs)).str()); + w.clear(); EXPECT_EQ("ncs", (w << std::string(ncs)).str()); + + // This should not compile: +#if 0 + const wchar_t *wcs = L"wcs"; + + // w << (wchar_t)'x'; + // w << (const wchar_t *)wcs; + // w << (wchar_t *)wcs; + // w << fmt::WStringRef(wcs); + // w << std::wstring(wcs); +#endif +} + +TEST(WWriterTest, Stream) { + WMemoryWriter w; + + const wchar_t *wcs = L"wcs"; + + w.clear(); EXPECT_EQ(L"true", (w << true).str()); + w.clear(); EXPECT_EQ(L"false", (w << false).str()); + w.clear(); EXPECT_EQ(L"0", (w << (signed char)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (signed short)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (signed int)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (signed long)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (fmt::LongLong)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (unsigned char)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (unsigned short)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (unsigned int)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (unsigned long)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (fmt::ULongLong)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (float)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (double)0).str()); + w.clear(); EXPECT_EQ(L"0", (w << (long double)0).str()); + w.clear(); EXPECT_EQ(L"0x1234", (w << (const void*)0x1234).str()); + w.clear(); EXPECT_EQ(L"0x1234", (w << (void *)0x1234).str()); + w.clear(); EXPECT_EQ(L"x", (w << (wchar_t)'x').str()); + w.clear(); EXPECT_EQ(L"wcs", (w << (const wchar_t *)wcs).str()); + w.clear(); EXPECT_EQ(L"wcs", (w << (wchar_t *)wcs).str()); + w.clear(); EXPECT_EQ(L"wcs", (w << fmt::WStringRef(wcs)).str()); + w.clear(); EXPECT_EQ(L"wcs", (w << std::wstring(wcs)).str()); + + const char *ncs = "ncs"; + + w.clear(); EXPECT_EQ(L"x", (w << (char)'x').str()); + w.clear(); EXPECT_EQ(L"ncs", (w << (const char *)ncs).str()); + w.clear(); EXPECT_EQ(L"ncs", (w << (char *)ncs).str()); + w.clear(); EXPECT_EQ(L"ncs", (w << fmt::StringRef(ncs)).str()); + w.clear(); EXPECT_EQ(L"ncs", (w << std::string(ncs)).str()); } TEST(ArrayWriterTest, Ctor) { @@ -1404,7 +1507,10 @@ TEST(FormatterTest, FormatStringFromSpeedTest) { TEST(FormatterTest, FormatExamples) { using fmt::hex; - EXPECT_EQ("0000cafe", (MemoryWriter() << pad(hex(0xcafe), 8, '0')).str()); + { + MemoryWriter w; + EXPECT_EQ("0000cafe", (w << pad(hex(0xcafe), 8, '0')).str()); + } std::string message = format("The answer is {}", 42); EXPECT_EQ("The answer is 42", message); diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 0e84f527..e37310a3 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -111,13 +111,11 @@ 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())); @@ -137,7 +135,6 @@ TEST(OStreamTest, WriteToOStream) { EXPECT_EQ("foo", os.str()); } -#if __cplusplus >= 201103L TEST(OStreamTest, WriteUserDefinedTypeToOStream) { std::ostringstream os; fmt::MemoryWriter w; @@ -145,8 +142,12 @@ TEST(OStreamTest, WriteUserDefinedTypeToOStream) { w << "The answer is " << u; fmt::internal::write(os, w); EXPECT_EQ("The answer is 42", os.str()); -} + +#if FMT_USE_OSTREAM_RVALUE + EXPECT_EQ("The answer is 42", + (fmt::MemoryWriter() << "The answer is " << UserDefinedTest()).str()); #endif +} TEST(OStreamTest, WriteToOStreamMaxSize) { std::size_t max_size = std::numeric_limits::max(); @@ -192,11 +193,10 @@ TEST(OStreamTest, WriteToOStreamMaxSize) { fmt::internal::write(os, w); } -#if __cplusplus >= 201103L struct Xs { - const size_t size; + const int size; const std::string s; - Xs() : size(200), s(size, 'x') {} + Xs() : size(200), s(static_cast(size), 'x') {} }; inline std::ostream& operator<<(std::ostream& os, Xs const& xs) { @@ -209,8 +209,7 @@ TEST(OStreamTest, FormatBuf1) { int n = fmt::internal::INLINE_BUFFER_SIZE / xs.size + 1; for (int i = 0; i < n; ++i) w << xs; - EXPECT_EQ(w.size(), size_t(n * xs.size)); + EXPECT_EQ(w.size(), static_cast(n * xs.size)); w << xs; - EXPECT_EQ(w.size(), size_t((n + 1) * xs.size)); + EXPECT_EQ(w.size(), static_cast((n + 1) * xs.size)); } -#endif From 5d9a88f83a5400e64baa5112d5af58ca609e0ba0 Mon Sep 17 00:00:00 2001 From: effzeh Date: Tue, 11 Apr 2017 20:57:22 +0200 Subject: [PATCH 2/5] Fix ostream-test --- test/ostream-test.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/ostream-test.cc b/test/ostream-test.cc index e37310a3..5e3a7d56 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -111,7 +111,10 @@ std::ostream &operator<<(std::ostream &os, EmptyTest) { return os << ""; } -struct UserDefinedTest { int i = 42; }; +struct UserDefinedTest { + int i; + UserDefinedTest() : i(42) {} +}; std::ostream &operator<<(std::ostream &os, const UserDefinedTest &u) { return os << u.i; From 7458bb7fe0f8a6db319432aec8866e510503a4a6 Mon Sep 17 00:00:00 2001 From: effzeh Date: Sat, 6 May 2017 13:43:07 +0200 Subject: [PATCH 3/5] Consistent behavior of operator<< The (templated) operator<< defined in ostream.h currently prevents efficient handling of some basic types in BasicWriter. E.g.: MemoryWriter w; w << std::string("hello"); calls this operator (if visible). This will unneccessarily construct a std::ostream object using a FormatBuf as its streambuf and insert the string into the std::ostream object. (The same is true for float and some other built-in types.) The write_str method in BasicWriter basically does the same using a cheap memcpy. Fix this performance problem by providing additional overloads of operator<<. --- fmt/format.h | 314 ++++++++++++++++++++------------------------ test/format-test.cc | 239 ++++++++++++++++----------------- 2 files changed, 259 insertions(+), 294 deletions(-) diff --git a/fmt/format.h b/fmt/format.h index 9803a27d..b0f77db1 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -1155,6 +1155,9 @@ struct NamedArgWithType; template struct Null {}; +template +struct Incomplete; // never defined. + // A helper class template to enable or disable overloads taking wide // characters and strings in MakeValue. template @@ -2590,10 +2593,6 @@ class BasicWriter : public BasicWriterBase { */ virtual ~BasicWriter() {} - void clear() FMT_NOEXCEPT { buffer_.clear(); } - - Buffer &buffer() FMT_NOEXCEPT { return buffer_; } - /** Returns the total number of characters written. */ @@ -2666,7 +2665,7 @@ class BasicWriter : public BasicWriterBase { write_int(spec.value(), spec); } - template // XXX: FloatFormatSpec? + template void append_double(Float value) { write_double(value, FormatSpec()); } @@ -2689,6 +2688,14 @@ class BasicWriter : public BasicWriterBase { buffer_.push_back(ch); } + void append_char( + typename internal::WCharHelper::Unsupported) { + // Use an incomplete type to generate a compiler error if this function is + // ever used. static_assert(false) does not work here. + internal::Incomplete + converting_wide_strings_to_narrow_strings_is_not_supported; + } + void append_str(StringRef str) { buffer_.append(str.data(), str.data() + str.size()); } @@ -2698,6 +2705,12 @@ class BasicWriter : public BasicWriterBase { buffer_.append(str.data(), str.data() + str.size()); } + void append_str( + typename internal::WCharHelper::Unsupported) { + internal::Incomplete + converting_wide_strings_to_narrow_strings_is_not_supported; + } + void append_str(const StrFormatSpec &spec) { const char *s = spec.str(); write_str(s, std::char_traits::length(s), spec); @@ -2710,110 +2723,139 @@ class BasicWriter : public BasicWriterBase { write_str(s, std::char_traits::length(s), spec); } - private: - // The following methods are private to disallow writing wide characters - // and strings to a char stream. If you want to print a wide string as a - // pointer as std::ostream does, cast it to const void*. - // Do not implement! - void append_char( - typename internal::WCharHelper::Unsupported); - void append_str( - typename internal::WCharHelper::Unsupported); void append_str( typename internal::WCharHelper< - const StrFormatSpec &, Char>::Unsupported); -}; + const StrFormatSpec &, Char>::Unsupported) { + internal::Incomplete + converting_wide_strings_to_narrow_strings_is_not_supported; + } -template -inline BasicWriter &operator<<(BasicWriter &w, bool value) { - // Note: - // Uses the same format as ArgFormatterBase. - w.append_str(value ? "true" : "false"); - return w; -} + BasicWriter &operator<<(bool value) { + append_str(value ? "true" : "false"); + return *this; + } -template -inline BasicWriter &operator<<(BasicWriter &w, signed char value) { - w.append_decimal(static_cast(value)); - return w; -} + BasicWriter &operator<<(signed char value) { + append_decimal(static_cast(value)); + return *this; + } -template -inline BasicWriter &operator<<(BasicWriter &w, short value) { - w.append_decimal(static_cast(value)); - return w; -} + BasicWriter &operator<<(short value) { + append_decimal(static_cast(value)); + return *this; + } -template -inline BasicWriter &operator<<(BasicWriter &w, int value) { - w.append_decimal(static_cast(value)); - return w; -} + BasicWriter &operator<<(int value) { + append_decimal(static_cast(value)); + return *this; + } -template -inline BasicWriter &operator<<(BasicWriter &w, long value) { - w.append_decimal(value); - return w; -} + BasicWriter &operator<<(long value) { + append_decimal(value); + return *this; + } -template -inline BasicWriter &operator<<(BasicWriter &w, LongLong value) { - w.append_decimal(value); - return w; -} + BasicWriter &operator<<(LongLong value) { + append_decimal(value); + return *this; + } #if USHRT_MAX <= INT_MAX + BasicWriter &operator<<(unsigned char value) { + append_decimal(static_cast(value)); + return *this; + } -template -inline BasicWriter &operator<<( - BasicWriter &w, unsigned char value) { - w.append_decimal(static_cast(value)); - return w; -} - -template -inline BasicWriter &operator<<( - BasicWriter &w, unsigned short value) { - w.append_decimal(static_cast(value)); - return w; -} - -#else // !( USHRT_MAX <= INT_MAX ) ----> - -template -inline BasicWriter &operator<<( - BasicWriter &w, unsigned char value) { - w.append_decimal(static_cast(value)); - return w; -} - -template -inline BasicWriter &operator<<( - BasicWriter &w, unsigned short value) { - w.append_decimal(static_cast(value)); - return w; -} + BasicWriter &operator<<(unsigned short value) { + append_decimal(static_cast(value)); + return *this; + } +#else + BasicWriter &operator<<(unsigned char value) { + append_decimal(static_cast(value)); + return *this; + } + BasicWriter &operator<<(unsigned short value) { + append_decimal(static_cast(value)); + return *this; + } #endif -template -inline BasicWriter &operator<<(BasicWriter &w, unsigned int value) { - w.append_decimal(value); - return w; -} + BasicWriter &operator<<(unsigned int value) { + append_decimal(value); + return *this; + } -template -inline BasicWriter &operator<<( - BasicWriter &w, unsigned long value) { - w.append_decimal(value); - return w; -} + BasicWriter &operator<<(unsigned long value) { + append_decimal(value); + return *this; + } -template -inline BasicWriter &operator<<(BasicWriter &w, ULongLong value) { - w.append_decimal(value); - return w; -} + BasicWriter &operator<<(ULongLong value) { + append_decimal(value); + return *this; + } + + BasicWriter &operator<<(float value) { + append_double(static_cast(value)); + return *this; + } + + BasicWriter &operator<<(double value) { + append_double(value); + return *this; + } + + BasicWriter &operator<<(long double value) { + append_double(value); + return *this; + } + + BasicWriter &operator<<(char ch) { + append_char(ch); + return *this; + } + + BasicWriter &operator<<(wchar_t ch) { + append_char(ch); + return *this; + } + + BasicWriter &operator<<(const char *value) { + append_str(fmt::StringRef(value)); + return *this; + } + + BasicWriter &operator<<(const wchar_t *value) { + append_str(fmt::WStringRef(value)); + return *this; + } + + BasicWriter &operator<<(char *value) { + append_str(fmt::StringRef(value)); + return *this; + } + + BasicWriter &operator<<(wchar_t *value) { + append_str(fmt::WStringRef(value)); + return *this; + } + + BasicWriter &operator<<(const void *pointer) { + append_pointer(pointer); + return *this; + } + + BasicWriter &operator<<(void *pointer) { + append_pointer(pointer); + return *this; + } + + void clear() FMT_NOEXCEPT { buffer_.clear(); } + + Buffer &buffer() FMT_NOEXCEPT { return buffer_; } +}; template inline BasicWriter &operator<<( @@ -2822,76 +2864,6 @@ inline BasicWriter &operator<<( return w; } -template -inline BasicWriter &operator<<(BasicWriter &w, float value) { - w.append_double(static_cast(value)); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, double value) { - w.append_double(value); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, long double value) { - w.append_double(value); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, char ch) { - w.append_char(ch); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, wchar_t ch) { - w.append_char(ch); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, const char *value) { - w.append_str(fmt::StringRef(value)); - return w; -} - -template -inline BasicWriter &operator<<( - BasicWriter &w, const wchar_t *value) { - w.append_str(fmt::WStringRef(value)); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, char *value) { - w.append_str(fmt::StringRef(value)); - return w; -} - -template -inline BasicWriter &operator<<(BasicWriter &w, wchar_t *value) { - w.append_str(fmt::WStringRef(value)); - return w; -} - -template -inline BasicWriter &operator<<( - BasicWriter &w, fmt::BasicStringRef s) { - w.append_str(s); - return w; -} - -template -inline BasicWriter &operator<<( - BasicWriter &w, - const std::basic_string, Alloc> &value) { - w.append_str(fmt::BasicStringRef(value)); - return w; -} - template inline BasicWriter &operator<<( BasicWriter &w, const StrFormatSpec &value) { @@ -2899,16 +2871,17 @@ inline BasicWriter &operator<<( return w; } -template +template inline BasicWriter &operator<<( - BasicWriter &w, const void *pointer) { - w.append_pointer(pointer); + BasicWriter &w, const fmt::BasicStringRef &s) { + w.append_str(s); return w; } -template -inline BasicWriter &operator<<(BasicWriter &w, void *pointer) { - w.append_pointer(pointer); +template +inline BasicWriter &operator<<( + BasicWriter &w, const std::basic_string &value) { + w.append_str(fmt::BasicStringRef(value)); return w; } @@ -2923,8 +2896,7 @@ template < typename = typename std::enable_if< !std::is_lvalue_reference::value && std::is_base_of::value - >::type, - typename = decltype(std::declval() << std::declval()) + >::type > inline WriterT &&operator<<(WriterT &&w, const T &value) { w << value; diff --git a/test/format-test.cc b/test/format-test.cc index 1c672c28..e1b22f92 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -119,13 +119,8 @@ void std_format(long double value, std::wstring &result) { // as writing it to std::basic_ostringstream. template ::testing::AssertionResult check_write(const T &value, const char *type) { -#if FMT_USE_OSTREAM_RVALUE std::basic_string actual = (fmt::BasicMemoryWriter() << value).str(); -#else - fmt::BasicMemoryWriter w; - std::basic_string actual = (w << value).str(); -#endif std::basic_string expected; std_format(value, expected); if (expected == actual) @@ -369,28 +364,25 @@ TEST(WriterTest, WriteWideString) { TEST(WriterTest, bin) { using fmt::bin; - MemoryWriter w; - w.clear(); EXPECT_EQ("1100101011111110", (w << bin(0xcafe)).str()); - w.clear(); EXPECT_EQ("1011101010111110", (w << bin(0xbabeu)).str()); - w.clear(); EXPECT_EQ("1101111010101101", (w << bin(0xdeadl)).str()); - w.clear(); EXPECT_EQ("1011111011101111", (w << bin(0xbeeful)).str()); - w.clear(); - EXPECT_EQ("11001010111111101011101010111110", (w << bin(0xcafebabell)).str()); - w.clear(); + EXPECT_EQ("1100101011111110", (MemoryWriter() << bin(0xcafe)).str()); + EXPECT_EQ("1011101010111110", (MemoryWriter() << bin(0xbabeu)).str()); + EXPECT_EQ("1101111010101101", (MemoryWriter() << bin(0xdeadl)).str()); + EXPECT_EQ("1011111011101111", (MemoryWriter() << bin(0xbeeful)).str()); + EXPECT_EQ("11001010111111101011101010111110", + (MemoryWriter() << bin(0xcafebabell)).str()); EXPECT_EQ("11011110101011011011111011101111", - (w << bin(0xdeadbeefull)).str()); + (MemoryWriter() << bin(0xdeadbeefull)).str()); } TEST(WriterTest, oct) { using fmt::oct; - MemoryWriter w; - w.clear(); EXPECT_EQ("12", (w << oct(static_cast(012))).str()); - w.clear(); EXPECT_EQ("12", (w << oct(012)).str()); - w.clear(); EXPECT_EQ("34", (w << oct(034u)).str()); - w.clear(); EXPECT_EQ("56", (w << oct(056l)).str()); - w.clear(); EXPECT_EQ("70", (w << oct(070ul)).str()); - w.clear(); EXPECT_EQ("1234", (w << oct(01234ll)).str()); - w.clear(); EXPECT_EQ("5670", (w << oct(05670ull)).str()); + EXPECT_EQ("12", (MemoryWriter() << oct(static_cast(012))).str()); + EXPECT_EQ("12", (MemoryWriter() << oct(012)).str()); + EXPECT_EQ("34", (MemoryWriter() << oct(034u)).str()); + EXPECT_EQ("56", (MemoryWriter() << oct(056l)).str()); + EXPECT_EQ("70", (MemoryWriter() << oct(070ul)).str()); + EXPECT_EQ("1234", (MemoryWriter() << oct(01234ll)).str()); + EXPECT_EQ("5670", (MemoryWriter() << oct(05670ull)).str()); } TEST(WriterTest, hex) { @@ -400,24 +392,22 @@ TEST(WriterTest, hex) { // This shouldn't compile: //fmt::IntFormatSpec > (*phex2)(short value) = hex; - MemoryWriter w; - w.clear(); EXPECT_EQ("cafe", (w << hex(0xcafe)).str()); - w.clear(); EXPECT_EQ("babe", (w << hex(0xbabeu)).str()); - w.clear(); EXPECT_EQ("dead", (w << hex(0xdeadl)).str()); - w.clear(); EXPECT_EQ("beef", (w << hex(0xbeeful)).str()); - w.clear(); EXPECT_EQ("cafebabe", (w << hex(0xcafebabell)).str()); - w.clear(); EXPECT_EQ("deadbeef", (w << hex(0xdeadbeefull)).str()); + EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str()); + EXPECT_EQ("babe", (MemoryWriter() << hex(0xbabeu)).str()); + EXPECT_EQ("dead", (MemoryWriter() << hex(0xdeadl)).str()); + EXPECT_EQ("beef", (MemoryWriter() << hex(0xbeeful)).str()); + EXPECT_EQ("cafebabe", (MemoryWriter() << hex(0xcafebabell)).str()); + EXPECT_EQ("deadbeef", (MemoryWriter() << hex(0xdeadbeefull)).str()); } TEST(WriterTest, hexu) { using fmt::hexu; - MemoryWriter w; - w.clear(); EXPECT_EQ("CAFE", (w << hexu(0xcafe)).str()); - w.clear(); EXPECT_EQ("BABE", (w << hexu(0xbabeu)).str()); - w.clear(); EXPECT_EQ("DEAD", (w << hexu(0xdeadl)).str()); - w.clear(); EXPECT_EQ("BEEF", (w << hexu(0xbeeful)).str()); - w.clear(); EXPECT_EQ("CAFEBABE", (w << hexu(0xcafebabell)).str()); - w.clear(); EXPECT_EQ("DEADBEEF", (w << hexu(0xdeadbeefull)).str()); + EXPECT_EQ("CAFE", (MemoryWriter() << hexu(0xcafe)).str()); + EXPECT_EQ("BABE", (MemoryWriter() << hexu(0xbabeu)).str()); + EXPECT_EQ("DEAD", (MemoryWriter() << hexu(0xdeadl)).str()); + EXPECT_EQ("BEEF", (MemoryWriter() << hexu(0xbeeful)).str()); + EXPECT_EQ("CAFEBABE", (MemoryWriter() << hexu(0xcafebabell)).str()); + EXPECT_EQ("DEADBEEF", (MemoryWriter() << hexu(0xdeadbeefull)).str()); } template @@ -442,22 +432,22 @@ public: ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); } TEST(WriterTest, pad) { - MemoryWriter w; using fmt::hex; - w.clear(); EXPECT_EQ(" cafe", (w << pad(hex(0xcafe), 8)).str()); - w.clear(); EXPECT_EQ(" babe", (w << pad(hex(0xbabeu), 8)).str()); - w.clear(); EXPECT_EQ(" dead", (w << pad(hex(0xdeadl), 8)).str()); - w.clear(); EXPECT_EQ(" beef", (w << pad(hex(0xbeeful), 8)).str()); - w.clear(); EXPECT_EQ(" dead", (w << pad(hex(0xdeadll), 8)).str()); - w.clear(); EXPECT_EQ(" beef", (w << pad(hex(0xbeefull), 8)).str()); + EXPECT_EQ(" cafe", (MemoryWriter() << pad(hex(0xcafe), 8)).str()); + EXPECT_EQ(" babe", (MemoryWriter() << pad(hex(0xbabeu), 8)).str()); + EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadl), 8)).str()); + EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeeful), 8)).str()); + EXPECT_EQ(" dead", (MemoryWriter() << pad(hex(0xdeadll), 8)).str()); + EXPECT_EQ(" beef", (MemoryWriter() << pad(hex(0xbeefull), 8)).str()); - w.clear(); EXPECT_EQ(" 11", (w << pad(11, 7)).str()); - w.clear(); EXPECT_EQ(" 22", (w << pad(22u, 7)).str()); - w.clear(); EXPECT_EQ(" 33", (w << pad(33l, 7)).str()); - w.clear(); EXPECT_EQ(" 44", (w << pad(44ul, 7)).str()); - w.clear(); EXPECT_EQ(" 33", (w << pad(33ll, 7)).str()); - w.clear(); EXPECT_EQ(" 44", (w << pad(44ull, 7)).str()); + EXPECT_EQ(" 11", (MemoryWriter() << pad(11, 7)).str()); + EXPECT_EQ(" 22", (MemoryWriter() << pad(22u, 7)).str()); + EXPECT_EQ(" 33", (MemoryWriter() << pad(33l, 7)).str()); + EXPECT_EQ(" 44", (MemoryWriter() << pad(44ul, 7)).str()); + EXPECT_EQ(" 33", (MemoryWriter() << pad(33ll, 7)).str()); + EXPECT_EQ(" 44", (MemoryWriter() << pad(44ull, 7)).str()); + MemoryWriter w; w.clear(); w << pad(42, 5, '0'); EXPECT_EQ("00042", w.str()); @@ -470,24 +460,21 @@ TEST(WriterTest, pad) { } TEST(WriterTest, PadString) { - MemoryWriter w; - w.clear(); EXPECT_EQ("test ", (w << pad("test", 8)).str()); - w.clear(); EXPECT_EQ("test******", (w << pad("test", 10, '*')).str()); + EXPECT_EQ("test ", (MemoryWriter() << pad("test", 8)).str()); + EXPECT_EQ("test******", (MemoryWriter() << pad("test", 10, '*')).str()); } TEST(WriterTest, PadWString) { - WMemoryWriter w; - w.clear(); EXPECT_EQ(L"test ", (w << pad(L"test", 8)).str()); - w.clear(); EXPECT_EQ(L"test******", (w << pad(L"test", 10, '*')).str()); - w.clear(); EXPECT_EQ(L"test******", (w << pad(L"test", 10, L'*')).str()); + EXPECT_EQ(L"test ", (WMemoryWriter() << pad(L"test", 8)).str()); + EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, '*')).str()); + EXPECT_EQ(L"test******", (WMemoryWriter() << pad(L"test", 10, L'*')).str()); } TEST(WriterTest, NoConflictWithIOManip) { using namespace std; using namespace fmt; - MemoryWriter w; - w.clear(); EXPECT_EQ("cafe", (w << hex(0xcafe)).str()); - w.clear(); EXPECT_EQ("12", (w << oct(012)).str()); + EXPECT_EQ("cafe", (MemoryWriter() << hex(0xcafe)).str()); + EXPECT_EQ("12", (MemoryWriter() << oct(012)).str()); } TEST(WriterTest, Format) { @@ -505,85 +492,94 @@ TEST(WriterTest, Format) { } TEST(WriterTest, WWriter) { - WMemoryWriter w; - EXPECT_EQ(L"cafe", (w << fmt::hex(0xcafe)).str()); + EXPECT_EQ(L"cafe", (fmt::WMemoryWriter() << fmt::hex(0xcafe)).str()); } TEST(WriterTest, Stream) { - MemoryWriter w; - const char *ncs = "ncs"; - w.clear(); EXPECT_EQ("true", (w << true).str()); - w.clear(); EXPECT_EQ("false", (w << false).str()); - w.clear(); EXPECT_EQ("0", (w << (signed char)0).str()); - w.clear(); EXPECT_EQ("0", (w << (signed short)0).str()); - w.clear(); EXPECT_EQ("0", (w << (signed int)0).str()); - w.clear(); EXPECT_EQ("0", (w << (signed long)0).str()); - w.clear(); EXPECT_EQ("0", (w << (fmt::LongLong)0).str()); - w.clear(); EXPECT_EQ("0", (w << (unsigned char)0).str()); - w.clear(); EXPECT_EQ("0", (w << (unsigned short)0).str()); - w.clear(); EXPECT_EQ("0", (w << (unsigned int)0).str()); - w.clear(); EXPECT_EQ("0", (w << (unsigned long)0).str()); - w.clear(); EXPECT_EQ("0", (w << (fmt::ULongLong)0).str()); - w.clear(); EXPECT_EQ("0", (w << (float)0).str()); - w.clear(); EXPECT_EQ("0", (w << (double)0).str()); - w.clear(); EXPECT_EQ("0", (w << (long double)0).str()); - w.clear(); EXPECT_EQ("0x1234", (w << (const void *)0x1234).str()); - w.clear(); EXPECT_EQ("0x1234", (w << (void *)0x1234).str()); - w.clear(); EXPECT_EQ("x", (w << (char)'x').str()); - w.clear(); EXPECT_EQ("ncs", (w << (const char *)ncs).str()); - w.clear(); EXPECT_EQ("ncs", (w << (char *)ncs).str()); - w.clear(); EXPECT_EQ("ncs", (w << fmt::StringRef(ncs)).str()); - w.clear(); EXPECT_EQ("ncs", (w << std::string(ncs)).str()); + EXPECT_EQ("true", (MemoryWriter() << true).str()); + EXPECT_EQ("false", (MemoryWriter() << false).str()); + EXPECT_EQ("0", (MemoryWriter() << (signed char)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (signed short)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (signed int)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (signed long)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (fmt::LongLong)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (unsigned char)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (unsigned short)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (unsigned int)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (unsigned long)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (fmt::ULongLong)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (float)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (double)0).str()); + EXPECT_EQ("0", (MemoryWriter() << (long double)0).str()); + EXPECT_EQ("0x1234", (MemoryWriter() << (const void *)0x1234).str()); + EXPECT_EQ("0x1234", (MemoryWriter() << (void *)0x1234).str()); + EXPECT_EQ("x", (MemoryWriter() << (char)'x').str()); + EXPECT_EQ("ncs", (MemoryWriter() << (const char *)ncs).str()); + EXPECT_EQ("ncs", (MemoryWriter() << (char *)ncs).str()); + EXPECT_EQ("ncs", (MemoryWriter() << fmt::StringRef(ncs)).str()); + EXPECT_EQ("ncs", (MemoryWriter() << std::string(ncs)).str()); + EXPECT_EQ("0", (MemoryWriter() << fmt::IntFormatSpec(0)).str()); + EXPECT_EQ("ncs", (MemoryWriter() << fmt::StrFormatSpec(ncs, 3, ' ')).str()); // This should not compile: #if 0 const wchar_t *wcs = L"wcs"; + MemoryWriter w; + //w << (wchar_t)'x'; + //w << (const wchar_t *)wcs; + //w << (wchar_t *)wcs; + //w << fmt::WStringRef(wcs); + //w << std::wstring(wcs); + w << fmt::StrFormatSpec(wcs, 3, ' '); +#endif - // w << (wchar_t)'x'; - // w << (const wchar_t *)wcs; - // w << (wchar_t *)wcs; - // w << fmt::WStringRef(wcs); - // w << std::wstring(wcs); +#if FMT_USE_OSTREAM_RVALUE + EXPECT_EQ("i = 123", (MemoryWriter() << "i = " << 123).str()); #endif } TEST(WWriterTest, Stream) { - WMemoryWriter w; - const wchar_t *wcs = L"wcs"; - w.clear(); EXPECT_EQ(L"true", (w << true).str()); - w.clear(); EXPECT_EQ(L"false", (w << false).str()); - w.clear(); EXPECT_EQ(L"0", (w << (signed char)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (signed short)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (signed int)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (signed long)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (fmt::LongLong)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (unsigned char)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (unsigned short)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (unsigned int)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (unsigned long)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (fmt::ULongLong)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (float)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (double)0).str()); - w.clear(); EXPECT_EQ(L"0", (w << (long double)0).str()); - w.clear(); EXPECT_EQ(L"0x1234", (w << (const void*)0x1234).str()); - w.clear(); EXPECT_EQ(L"0x1234", (w << (void *)0x1234).str()); - w.clear(); EXPECT_EQ(L"x", (w << (wchar_t)'x').str()); - w.clear(); EXPECT_EQ(L"wcs", (w << (const wchar_t *)wcs).str()); - w.clear(); EXPECT_EQ(L"wcs", (w << (wchar_t *)wcs).str()); - w.clear(); EXPECT_EQ(L"wcs", (w << fmt::WStringRef(wcs)).str()); - w.clear(); EXPECT_EQ(L"wcs", (w << std::wstring(wcs)).str()); + EXPECT_EQ(L"true", (WMemoryWriter() << true).str()); + EXPECT_EQ(L"false", (WMemoryWriter() << false).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (signed char)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (signed short)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (signed int)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (signed long)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (fmt::LongLong)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (unsigned char)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (unsigned short)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (unsigned int)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (unsigned long)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (fmt::ULongLong)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (float)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (double)0).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << (long double)0).str()); + EXPECT_EQ(L"0x1234", (WMemoryWriter() << (const void*)0x1234).str()); + EXPECT_EQ(L"0x1234", (WMemoryWriter() << (void *)0x1234).str()); + EXPECT_EQ(L"x", (WMemoryWriter() << (wchar_t)'x').str()); + EXPECT_EQ(L"wcs", (WMemoryWriter() << (const wchar_t *)wcs).str()); + EXPECT_EQ(L"wcs", (WMemoryWriter() << (wchar_t *)wcs).str()); + EXPECT_EQ(L"wcs", (WMemoryWriter() << fmt::WStringRef(wcs)).str()); + EXPECT_EQ(L"wcs", (WMemoryWriter() << std::wstring(wcs)).str()); + EXPECT_EQ(L"0", (WMemoryWriter() << fmt::IntFormatSpec(0)).str()); + EXPECT_EQ(L"wcs", (WMemoryWriter() << fmt::StrFormatSpec(wcs, 3, ' ')).str()); const char *ncs = "ncs"; - w.clear(); EXPECT_EQ(L"x", (w << (char)'x').str()); - w.clear(); EXPECT_EQ(L"ncs", (w << (const char *)ncs).str()); - w.clear(); EXPECT_EQ(L"ncs", (w << (char *)ncs).str()); - w.clear(); EXPECT_EQ(L"ncs", (w << fmt::StringRef(ncs)).str()); - w.clear(); EXPECT_EQ(L"ncs", (w << std::string(ncs)).str()); + EXPECT_EQ(L"x", (WMemoryWriter() << (char)'x').str()); + EXPECT_EQ(L"ncs", (WMemoryWriter() << (const char *)ncs).str()); + EXPECT_EQ(L"ncs", (WMemoryWriter() << (char *)ncs).str()); + EXPECT_EQ(L"ncs", (WMemoryWriter() << fmt::StringRef(ncs)).str()); + EXPECT_EQ(L"ncs", (WMemoryWriter() << std::string(ncs)).str()); + EXPECT_EQ(L"ncs", (WMemoryWriter() << fmt::StrFormatSpec(ncs, 3, ' ')).str()); + +#if FMT_USE_OSTREAM_RVALUE + EXPECT_EQ(L"i = 123", (WMemoryWriter() << L"i = " << 123).str()); +#endif } TEST(ArrayWriterTest, Ctor) { @@ -1507,10 +1503,7 @@ TEST(FormatterTest, FormatStringFromSpeedTest) { TEST(FormatterTest, FormatExamples) { using fmt::hex; - { - MemoryWriter w; - EXPECT_EQ("0000cafe", (w << pad(hex(0xcafe), 8, '0')).str()); - } + EXPECT_EQ("0000cafe", (MemoryWriter() << pad(hex(0xcafe), 8, '0')).str()); std::string message = format("The answer is {}", 42); EXPECT_EQ("The answer is 42", message); From 47f412bf8b2b6c688ec32a78fbef782853c7a70b Mon Sep 17 00:00:00 2001 From: effzeh Date: Sat, 6 May 2017 15:12:28 +0200 Subject: [PATCH 4/5] Implement templated operator<< as non-member functions only for MSVC --- fmt/format.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/fmt/format.h b/fmt/format.h index b0f77db1..fc2dcd8f 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -2111,7 +2111,7 @@ class FormatterBase { template void write(BasicWriter &w, const Char *start, const Char *end) { if (start != end) - w << BasicStringRef(start, internal::to_unsigned(end - start)); + w.append_str(BasicStringRef(start, internal::to_unsigned(end - start))); } }; } // namespace internal @@ -2852,11 +2852,54 @@ class BasicWriter : public BasicWriterBase { return *this; } + // MSVC complains about ambiguous operator<< if these templates are defined as + // member functions and an operator<<(Writer&, const T&) is visible, e.g. + // if ostream.h is included. + // + // MSVC also has a language extension which can convert rvalues to lvalues and + // which allows e.g. + // MemoryWriter() << bin(123); + // to work (calls the non-member function). + // + // If they are defined as non-member functions, GCC in C++98 mode cannot call + // them in cases like the one above. + // In C++11 mode, the overload defined below is used. This is only fast, if + // moving a BasicWriter is fast and this is, e.g., not the case for a + // MemoryWriter which uses its inline buffer. So it seems beneficial to + // implement operator<< as member functions. + +#ifndef _MSC_VER + template + BasicWriter &operator<<(const IntFormatSpec &value) { + append_int(value); + return *this; + } + + template + BasicWriter &operator<<(const StrFormatSpec &value) { + append_str(value); + return *this; + } + + template + BasicWriter &operator<<(const fmt::BasicStringRef &s) { + append_str(s); + return *this; + } + + template + BasicWriter &operator<<(const std::basic_string &value) { + append_str(fmt::BasicStringRef(value)); + return *this; + } +#endif + void clear() FMT_NOEXCEPT { buffer_.clear(); } Buffer &buffer() FMT_NOEXCEPT { return buffer_; } }; +#ifdef _MSC_VER template inline BasicWriter &operator<<( BasicWriter &w, const IntFormatSpec &value) { @@ -2884,6 +2927,7 @@ inline BasicWriter &operator<<( w.append_str(fmt::BasicStringRef(value)); return w; } +#endif #if FMT_USE_OSTREAM_RVALUE // http://cplusplus.github.io/LWG/lwg-active.html#1203 From 3abcd60f96e4c5d1680b23e8d37a09c914b24627 Mon Sep 17 00:00:00 2001 From: effzeh Date: Sat, 6 May 2017 15:32:14 +0200 Subject: [PATCH 5/5] Remove operator<<(rvalue, T) For MSVC this was a better than the rvalue->lvalue conversion --- fmt/format.h | 41 +---------------------------------------- test/format-test.cc | 8 -------- test/ostream-test.cc | 5 ----- 3 files changed, 1 insertion(+), 53 deletions(-) diff --git a/fmt/format.h b/fmt/format.h index fc2dcd8f..48704d18 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -155,24 +155,6 @@ typedef __int64 intmax_t; # endif #endif -#ifndef FMT_USE_OSTREAM_RVALUE -// If non-zero, enables operator<< for BasicWriter&&, such that things like -// std::string s = (MemoryWriter() << "i = " << i).str(); -// are possible. -// -// (See: http://cplusplus.github.io/LWG/lwg-active.html#1203) -// -// XXX: -// I don't know if FMT_USE_RVALUE_REFERENCES is the correct way to enable -// this... It works for g++ 6.3 and VC15, though. -// -# if FMT_USE_RVALUE_REFERENCES -# define FMT_USE_OSTREAM_RVALUE 1 -# else -# define FMT_USE_OSTREAM_RVALUE 0 -# endif -#endif - // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 @@ -2463,8 +2445,6 @@ class SystemError : public internal::RuntimeError { FMT_API void format_system_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; -class BasicWriterBase {}; - /** \rst This template provides operations for formatting and writing data into @@ -2484,7 +2464,7 @@ class BasicWriterBase {}; \endrst */ template -class BasicWriter : public BasicWriterBase { +class BasicWriter { private: // Output buffer. Buffer &buffer_; @@ -2929,25 +2909,6 @@ inline BasicWriter &operator<<( } #endif -#if FMT_USE_OSTREAM_RVALUE -// http://cplusplus.github.io/LWG/lwg-active.html#1203 -// -// For: -// std::string s = (MemoryWriter() << "i = " << i).str(); -template < - typename WriterT, - typename T, - typename = typename std::enable_if< - !std::is_lvalue_reference::value - && std::is_base_of::value - >::type -> -inline WriterT &&operator<<(WriterT &&w, const T &value) { - w << value; - return std::move(w); -} -#endif - template template typename BasicWriter::CharPtr BasicWriter::write_str( diff --git a/test/format-test.cc b/test/format-test.cc index e1b22f92..ab37cb44 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -534,10 +534,6 @@ TEST(WriterTest, Stream) { //w << std::wstring(wcs); w << fmt::StrFormatSpec(wcs, 3, ' '); #endif - -#if FMT_USE_OSTREAM_RVALUE - EXPECT_EQ("i = 123", (MemoryWriter() << "i = " << 123).str()); -#endif } TEST(WWriterTest, Stream) { @@ -576,10 +572,6 @@ TEST(WWriterTest, Stream) { EXPECT_EQ(L"ncs", (WMemoryWriter() << fmt::StringRef(ncs)).str()); EXPECT_EQ(L"ncs", (WMemoryWriter() << std::string(ncs)).str()); EXPECT_EQ(L"ncs", (WMemoryWriter() << fmt::StrFormatSpec(ncs, 3, ' ')).str()); - -#if FMT_USE_OSTREAM_RVALUE - EXPECT_EQ(L"i = 123", (WMemoryWriter() << L"i = " << 123).str()); -#endif } TEST(ArrayWriterTest, Ctor) { diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 5e3a7d56..c915ac7b 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -145,11 +145,6 @@ TEST(OStreamTest, WriteUserDefinedTypeToOStream) { w << "The answer is " << u; fmt::internal::write(os, w); EXPECT_EQ("The answer is 42", os.str()); - -#if FMT_USE_OSTREAM_RVALUE - EXPECT_EQ("The answer is 42", - (fmt::MemoryWriter() << "The answer is " << UserDefinedTest()).str()); -#endif } TEST(OStreamTest, WriteToOStreamMaxSize) {