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