diff --git a/fmt/format.cc b/fmt/format.cc index d63c5543..63e58842 100644 --- a/fmt/format.cc +++ b/fmt/format.cc @@ -371,7 +371,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 f4ca4c95..f5e2abac 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -1138,6 +1138,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 @@ -2094,7 +2097,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 @@ -2545,14 +2548,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) { @@ -2643,101 +2638,281 @@ 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); + + template + void append_int(const IntFormatSpec &spec) { + internal::CharTraits::convert(FillChar()); + write_int(spec.value(), spec); } - BasicWriter &operator<<(long value) { - write_decimal(value); - return *this; + + template + void append_double(Float value) { + write_double(value, FormatSpec()); } - BasicWriter &operator<<(unsigned long value) { - return *this << IntFormatSpec(value); + + 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); } - BasicWriter &operator<<(LongLong value) { - write_decimal(value); + + void append_char(char ch) { + buffer_.push_back(ch); + } + + void append_char( + typename internal::WCharHelper::Supported ch) { + 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()); + } + + void append_str( + typename internal::WCharHelper::Supported str) { + 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); + } + + 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); + } + + void append_str( + typename internal::WCharHelper< + const StrFormatSpec &, Char>::Unsupported) { + internal::Incomplete + converting_wide_strings_to_narrow_strings_is_not_supported; + } + + BasicWriter &operator<<(bool value) { + append_str(value ? "true" : "false"); + return *this; + } + + BasicWriter &operator<<(signed char value) { + append_decimal(static_cast(value)); + return *this; + } + + BasicWriter &operator<<(short value) { + append_decimal(static_cast(value)); + return *this; + } + + BasicWriter &operator<<(int value) { + append_decimal(static_cast(value)); + return *this; + } + + BasicWriter &operator<<(long value) { + append_decimal(value); + return *this; + } + + 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; + } + + 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 + + BasicWriter &operator<<(unsigned int value) { + append_decimal(value); + return *this; + } + + BasicWriter &operator<<(unsigned long value) { + append_decimal(value); return *this; } - /** - \rst - Formats *value* and writes it to the stream. - \endrst - */ BasicWriter &operator<<(ULongLong value) { - return *this << IntFormatSpec(value); + append_decimal(value); + return *this; + } + + BasicWriter &operator<<(float value) { + append_double(static_cast(value)); + return *this; } BasicWriter &operator<<(double value) { - write_double(value, FormatSpec()); + append_double(value); 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()); + append_double(value); return *this; } - /** - Writes a character to the stream. - */ - BasicWriter &operator<<(char value) { - buffer_.push_back(value); + BasicWriter &operator<<(char ch) { + append_char(ch); return *this; } - BasicWriter &operator<<( - typename internal::WCharHelper::Supported value) { - buffer_.push_back(value); + BasicWriter &operator<<(wchar_t ch) { + append_char(ch); 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()); + BasicWriter &operator<<(const char *value) { + append_str(fmt::StringRef(value)); return *this; } - BasicWriter &operator<<( - typename internal::WCharHelper::Supported value) { - const char *str = value.data(); - buffer_.append(str, str + value.size()); + BasicWriter &operator<<(const wchar_t *value) { + append_str(fmt::WStringRef(value)); return *this; } - template - BasicWriter &operator<<(IntFormatSpec spec) { - internal::CharTraits::convert(FillChar()); - write_int(spec.value(), spec); + BasicWriter &operator<<(char *value) { + append_str(fmt::StringRef(value)); return *this; } - template - BasicWriter &operator<<(const StrFormatSpec &spec) { - const StrChar *s = spec.str(); - write_str(s, std::char_traits::length(s), spec); + 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; + } + + // 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) { + w.append_int(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 fmt::BasicStringRef &s) { + w.append_str(s); + return w; +} + +template +inline BasicWriter &operator<<( + BasicWriter &w, const std::basic_string &value) { + w.append_str(fmt::BasicStringRef(value)); + return w; +} +#endif + template template typename BasicWriter::CharPtr BasicWriter::write_str( diff --git a/fmt/ostream.h b/fmt/ostream.h index 06b6ab56..1db1fcb4 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 852c91da..81ac7c30 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. @@ -483,6 +495,85 @@ TEST(WriterTest, WWriter) { EXPECT_EQ(L"cafe", (fmt::WMemoryWriter() << fmt::hex(0xcafe)).str()); } +TEST(WriterTest, Stream) { + const char *ncs = "ncs"; + + 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 +} + +TEST(WWriterTest, Stream) { + const wchar_t *wcs = L"wcs"; + + 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"; + + 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()); +} + TEST(ArrayWriterTest, Ctor) { char array[10] = "garbage"; fmt::ArrayWriter w(array, sizeof(array)); diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 0e84f527..c915ac7b 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -111,13 +111,14 @@ std::ostream &operator<<(std::ostream &os, EmptyTest) { return os << ""; } -#if __cplusplus >= 201103L -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; } -#endif TEST(OStreamTest, EmptyCustomOutput) { EXPECT_EQ("", fmt::format("{}", EmptyTest())); @@ -137,7 +138,6 @@ TEST(OStreamTest, WriteToOStream) { EXPECT_EQ("foo", os.str()); } -#if __cplusplus >= 201103L TEST(OStreamTest, WriteUserDefinedTypeToOStream) { std::ostringstream os; fmt::MemoryWriter w; @@ -146,7 +146,6 @@ TEST(OStreamTest, WriteUserDefinedTypeToOStream) { 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::max(); @@ -192,11 +191,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 +207,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