diff --git a/format.cc b/format.cc index 15c115f1..f58ef618 100644 --- a/format.cc +++ b/format.cc @@ -73,41 +73,34 @@ inline int IsInf(double x) { return !_finite(x); } #define FMT_SNPRINTF sprintf_s #endif // _MSC_VER +} -template -struct CharTraits; - -template <> -struct CharTraits { - template - static int FormatFloat(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } +template +int fmt::internal::CharTraits::FormatFloat( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, T value) { + if (width == 0) { return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); } -}; + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); +} -template <> -struct CharTraits { - template - static int FormatFloat(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - swprintf(buffer, size, format, value) : - swprintf(buffer, size, format, precision, value); - } +template +int fmt::internal::CharTraits::FormatFloat( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, T value) { + if (width == 0) { return precision < 0 ? - swprintf(buffer, size, format, width, value) : - swprintf(buffer, size, format, width, precision, value); + swprintf(buffer, size, format, value) : + swprintf(buffer, size, format, precision, value); } -}; + return precision < 0 ? + swprintf(buffer, size, format, width, value) : + swprintf(buffer, size, format, width, precision, value); } const char fmt::internal::DIGITS[] = @@ -310,7 +303,7 @@ void fmt::BasicWriter::FormatDouble( for (;;) { std::size_t size = buffer_.capacity() - offset; Char *start = &buffer_[offset]; - int n = CharTraits::FormatFloat( + int n = internal::CharTraits::FormatFloat( start, size, format, width_for_sprintf, precision, value); if (n >= 0 && offset + n < buffer_.capacity()) { if (sign) { diff --git a/format.h b/format.h index 770471f8..936c5095 100644 --- a/format.h +++ b/format.h @@ -148,6 +148,27 @@ void Array::append(const T *begin, const T *end) { size_ += num_elements; } +template +struct CharTraits; + +template <> +struct CharTraits { + typedef wchar_t UnsupportedType; + + template + static int FormatFloat(char *buffer, std::size_t size, + const char *format, unsigned width, int precision, T value); +}; + +template <> +struct CharTraits { + typedef char UnsupportedType; + + template + static int FormatFloat(wchar_t *buffer, std::size_t size, + const wchar_t *format, unsigned width, int precision, T value); +}; + // Information about an integer type. // IntTraits is not specialized for integer types smaller than int, // since these are promoted to int. @@ -501,6 +522,12 @@ class BasicWriter { CharPtr FormatString(const StringChar *s, std::size_t size, const FormatSpec &spec); + // This method is private to disallow writing a wide string to a + // char stream and vice versa. 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<<(const typename internal::CharTraits::UnsupportedType *); + public: /** Returns the number of characters written to the output buffer. @@ -596,13 +623,13 @@ class BasicWriter { return *this; } - BasicWriter &operator<<(Char value) { + BasicWriter &operator<<(char value) { *GrowBuffer(1) = value; return *this; } - BasicWriter &operator<<(const fmt::StringRef value) { - const char *str = value.c_str(); + BasicWriter &operator<<(const fmt::BasicStringRef value) { + const Char *str = value.c_str(); std::size_t size = value.size(); std::copy(str, str + size, GrowBuffer(size)); return *this; diff --git a/format_test.cc b/format_test.cc index c7113432..c9e8357c 100644 --- a/format_test.cc +++ b/format_test.cc @@ -89,22 +89,50 @@ using fmt::pad; FORMAT_TEST_THROW_(statement, expected_exception, expected_message, \ GTEST_NONFATAL_FAILURE_) -struct WriteChecker { +// Checks if writing value to BasicWriter produces the same result +// as writing it to std::basic_ostringstream. +template +static ::testing::AssertionResult CheckWrite(const T &value, const char *type) { + std::basic_ostringstream os; + os << value; + std::basic_string expected = os.str(); + std::basic_string actual = str(BasicWriter() << value); + if (expected == actual) + return ::testing::AssertionSuccess(); + return ::testing::AssertionFailure() + << "Value of: str(Writer<" << type << ">() << value)\n" + << " Actual: " << actual << "\n" + << "Expected: " << expected << "\n"; +} + +struct AnyWriteChecker { template ::testing::AssertionResult operator()(const char *, const T &value) const { - std::ostringstream os; - os << value; - std::string expected = os.str(), actual = str(Writer() << value); - if (expected == actual) - return ::testing::AssertionSuccess(); - return ::testing::AssertionFailure() - << "Value of: str(Writer() << value)\n" - << " Actual: " << actual << "\n" - << "Expected: " << expected << "\n"; + ::testing::AssertionResult result = CheckWrite(value, "char"); + return result ? CheckWrite(value, "wchar_t") : result; } }; -#define CHECK_WRITE(value) ASSERT_PRED_FORMAT1(WriteChecker(), value) +struct CharWriteChecker { + template + ::testing::AssertionResult operator()(const char *, const T &value) const { + return CheckWrite(value, "char"); + } +}; + +struct WCharWriteChecker { + template + ::testing::AssertionResult operator()(const char *, const T &value) const { + return CheckWrite(value, "char"); + } +}; + +// Checks if writing value to BasicWriter produces the same result +// as writing it to std::ostringstream both for char and wchar_t. +#define CHECK_WRITE(value) ASSERT_PRED_FORMAT1(AnyWriteChecker(), value) + +#define CHECK_WRITE_CHAR(value) ASSERT_PRED_FORMAT1(CharWriteChecker(), value) +#define CHECK_WRITE_WCHAR(value) ASSERT_PRED_FORMAT1(WCharWriteChecker(), value) // Increment a number in a string. void Increment(char *s) { @@ -279,8 +307,20 @@ TEST(WriterTest, WriteDouble) { CHECK_WRITE(4.2l); } +TEST(WriterTest, WriteChar) { + CHECK_WRITE('a'); +} + TEST(WriterTest, WriteString) { - CHECK_WRITE("abc"); + CHECK_WRITE_CHAR("abc"); + // The following line shouldn't compile: + //fmt::Writer() << L"abc"; +} + +TEST(WriterTest, WriteWideString) { + CHECK_WRITE_WCHAR(L"abc"); + // The following line shouldn't compile: + //fmt::WWriter() << "abc"; } TEST(WriterTest, oct) {