diff --git a/format.cc b/format.cc index f43ddf18..a0ba02df 100644 --- a/format.cc +++ b/format.cc @@ -64,32 +64,6 @@ using fmt::StringRef; namespace { -template -struct IsLongDouble { enum {VALUE = 0}; }; - -template <> -struct IsLongDouble { enum {VALUE = 1}; }; - -const char DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -// Fills the padding around the content and returns the pointer to the -// content area. -char *FillPadding(char *buffer, - unsigned total_size, std::size_t content_size, char fill) { - std::size_t padding = total_size - content_size; - std::size_t left_padding = padding / 2; - std::fill_n(buffer, left_padding, fill); - buffer += left_padding; - char *content = buffer; - std::fill_n(buffer + content_size, padding - left_padding, fill); - return content; -} - #ifdef _MSC_VER int SignBit(double value) { if (value < 0) return 1; @@ -101,237 +75,23 @@ int SignBit(double value) { #endif } -void BasicFormatter::FormatDecimal( - char *buffer, uint64_t value, unsigned num_digits) { - --num_digits; - while (value >= 100) { - // Integer division is slow so do it for a group of two digits instead - // of for every digit. The idea comes from the talk by Alexandrescu - // "Three Optimization Tips for C++". See speed-test for a comparison. - unsigned index = (value % 100) * 2; - value /= 100; - buffer[num_digits] = DIGITS[index + 1]; - buffer[num_digits - 1] = DIGITS[index]; - num_digits -= 2; - } - if (value < 10) { - *buffer = static_cast('0' + value); - return; - } - unsigned index = static_cast(value * 2); - buffer[1] = DIGITS[index + 1]; - buffer[0] = DIGITS[index]; -} +const char fmt::internal::DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; -void BasicFormatter::ReportUnknownType(char code, const char *type) { +void fmt::internal::ReportUnknownType(char code, const char *type) { if (std::isprint(static_cast(code))) { throw fmt::FormatError(fmt::str( - fmt::Format("unknown format code '{0}' for {1}") << code << type)); + fmt::Format("unknown format code '{}' for {}") << code << type)); } throw fmt::FormatError( - fmt::str(fmt::Format("unknown format code '\\x{0:02x}' for {1}") + fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {}") << static_cast(code) << type)); } -char *BasicFormatter::PrepareFilledBuffer( - unsigned size, const AlignSpec &spec, char sign) { - unsigned width = spec.width(); - if (width <= size) { - char *p = GrowBuffer(size); - *p = sign; - return p + size - 1; - } - char *p = GrowBuffer(width); - char *end = p + width; - Alignment align = spec.align(); - if (align == ALIGN_LEFT) { - *p = sign; - p += size; - std::fill(p, end, spec.fill()); - } else if (align == ALIGN_CENTER) { - p = FillPadding(p, width, size, spec.fill()); - *p = sign; - p += size; - } else { - if (align == ALIGN_NUMERIC) { - if (sign) { - *p++ = sign; - --size; - } - } else { - *(end - size) = sign; - } - std::fill(p, end - size, spec.fill()); - p = end; - } - return p - 1; -} - -template -void BasicFormatter::FormatDouble( - T value, const FormatSpec &spec, int precision) { - // Check type. - char type = spec.type(); - bool upper = false; - switch (type) { - case 0: - type = 'g'; - break; - case 'e': case 'f': case 'g': - break; - case 'F': -#ifdef _MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - // Fall through. - case 'E': case 'G': - upper = true; - break; - default: - ReportUnknownType(type, "double"); - break; - } - - char sign = 0; - // Use SignBit instead of value < 0 because the latter is always - // false for NaN. - if (SignBit(value)) { - sign = '-'; - value = -value; - } else if (spec.sign_flag()) { - sign = spec.plus_flag() ? '+' : ' '; - } - - if (value != value) { - // Format NaN ourselves because sprintf's output is not consistent - // across platforms. - std::size_t size = 4; - const char *nan = upper ? " NAN" : " nan"; - if (!sign) { - --size; - ++nan; - } - char *out = FormatString(nan, size, spec); - if (sign) - *out = sign; - return; - } - - if (isinf(value)) { - // Format infinity ourselves because sprintf's output is not consistent - // across platforms. - std::size_t size = 4; - const char *inf = upper ? " INF" : " inf"; - if (!sign) { - --size; - ++inf; - } - char *out = FormatString(inf, size, spec); - if (sign) - *out = sign; - return; - } - - size_t offset = buffer_.size(); - unsigned width = spec.width(); - if (sign) { - buffer_.reserve(buffer_.size() + std::max(width, 1u)); - if (width > 0) - --width; - ++offset; - } - - // Build format string. - enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - char format[MAX_FORMAT_SIZE]; - char *format_ptr = format; - *format_ptr++ = '%'; - unsigned width_for_sprintf = width; - if (spec.hash_flag()) - *format_ptr++ = '#'; - if (spec.align() == ALIGN_CENTER) { - width_for_sprintf = 0; - } else { - if (spec.align() == ALIGN_LEFT) - *format_ptr++ = '-'; - if (width != 0) - *format_ptr++ = '*'; - } - if (precision >= 0) { - *format_ptr++ = '.'; - *format_ptr++ = '*'; - } - if (IsLongDouble::VALUE) - *format_ptr++ = 'L'; - *format_ptr++ = type; - *format_ptr = '\0'; - - // Format using snprintf. - for (;;) { - size_t size = buffer_.capacity() - offset; - int n = 0; - char *start = &buffer_[offset]; - if (width_for_sprintf == 0) { - n = precision < 0 ? - snprintf(start, size, format, value) : - snprintf(start, size, format, precision, value); - } else { - n = precision < 0 ? - snprintf(start, size, format, width_for_sprintf, value) : - snprintf(start, size, format, width_for_sprintf, precision, value); - } - if (n >= 0 && offset + n < buffer_.capacity()) { - if (sign) { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') { - *(start - 1) = sign; - sign = 0; - } else { - *(start - 1) = spec.fill(); - } - ++n; - } - if (spec.align() == ALIGN_CENTER && - spec.width() > static_cast(n)) { - char *p = GrowBuffer(spec.width()); - std::copy(p, p + n, p + (spec.width() - n) / 2); - FillPadding(p, spec.width(), n, spec.fill()); - return; - } - if (spec.fill() != ' ' || sign) { - while (*start == ' ') - *start++ = spec.fill(); - if (sign) - *(start - 1) = sign; - } - GrowBuffer(n); - return; - } - buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity()); - } -} - -char *BasicFormatter::FormatString( - const char *s, std::size_t size, const FormatSpec &spec) { - char *out = 0; - if (spec.width() > size) { - out = GrowBuffer(spec.width()); - if (spec.align() == ALIGN_RIGHT) { - std::fill_n(out, spec.width() - size, spec.fill()); - out += spec.width() - size; - } else if (spec.align() == ALIGN_CENTER) { - out = FillPadding(out, spec.width(), size, spec.fill()); - } else { - std::fill_n(out + size, spec.width() - size, spec.fill()); - } - } else { - out = GrowBuffer(size); - } - std::copy(s, s + size, out); - return out; -} - // Throws Exception(message) if format contains '}', otherwise throws // FormatError reporting unmatched '{'. The idea is that unmatched '{' // should override other errors. @@ -573,7 +333,7 @@ void Formatter::DoFormat() { break; case CHAR: { if (spec.type_ && spec.type_ != 'c') - ReportUnknownType(spec.type_, "char"); + internal::ReportUnknownType(spec.type_, "char"); char *out = 0; if (spec.width_ > 1) { out = GrowBuffer(spec.width_); @@ -593,7 +353,7 @@ void Formatter::DoFormat() { } case STRING: { if (spec.type_ && spec.type_ != 's') - ReportUnknownType(spec.type_, "string"); + internal::ReportUnknownType(spec.type_, "string"); const char *str = arg.string.value; size_t size = arg.string.size; if (size == 0) { @@ -607,14 +367,14 @@ void Formatter::DoFormat() { } case POINTER: if (spec.type_ && spec.type_ != 'p') - ReportUnknownType(spec.type_, "pointer"); + internal::ReportUnknownType(spec.type_, "pointer"); spec.flags_= HASH_FLAG; spec.type_ = 'x'; FormatInt(reinterpret_cast(arg.pointer_value), spec); break; case CUSTOM: if (spec.type_) - ReportUnknownType(spec.type_, "object"); + internal::ReportUnknownType(spec.type_, "object"); (this->*arg.custom.format)(arg.custom.value, spec); break; default: diff --git a/format.h b/format.h index 7310bbc2..54eaeb64 100644 --- a/format.h +++ b/format.h @@ -136,6 +136,16 @@ struct IntTraits : SignedIntTraits {}; template <> struct IntTraits : SignedIntTraits {}; +template +struct IsLongDouble { enum {VALUE = 0}; }; + +template <> +struct IsLongDouble { enum {VALUE = 1}; }; + +extern const char DIGITS[]; + +void ReportUnknownType(char code, const char *type); + class ArgInserter; class FormatterProxy; } @@ -341,6 +351,7 @@ DEFINE_INT_FORMATTERS(long) DEFINE_INT_FORMATTERS(unsigned) DEFINE_INT_FORMATTERS(unsigned long) +template class BasicFormatter { private: // Returns the number of decimal digits in n. Trailing zeros are not counted @@ -360,29 +371,30 @@ class BasicFormatter { } } - static void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits); + static void FormatDecimal(Char *buffer, uint64_t value, unsigned num_digits); protected: - static void ReportUnknownType(char code, const char *type); + static Char *FillPadding(Char *buffer, + unsigned total_size, std::size_t content_size, char fill); enum { INLINE_BUFFER_SIZE = 500 }; - mutable internal::Array buffer_; // Output buffer. + mutable internal::Array buffer_; // Output buffer. // Grows the buffer by n characters and returns a pointer to the newly // allocated area. - char *GrowBuffer(std::size_t n) { + Char *GrowBuffer(std::size_t n) { std::size_t size = buffer_.size(); buffer_.resize(size + n); return &buffer_[size]; } - char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) { - char *p = GrowBuffer(size); + Char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) { + Char *p = GrowBuffer(size); *p = sign; return p + size - 1; } - char *PrepareFilledBuffer(unsigned size, const AlignSpec &spec, char sign); + Char *PrepareFilledBuffer(unsigned size, const AlignSpec &spec, char sign); // Formats an integer. template @@ -406,13 +418,13 @@ class BasicFormatter { Returns a pointer to the output buffer content. No terminating null character is appended. */ - const char *data() const { return &buffer_[0]; } + const Char *data() const { return &buffer_[0]; } /** Returns a pointer to the output buffer content with terminating null character appended. */ - const char *c_str() const { + const Char *c_str() const { std::size_t size = buffer_.size(); buffer_.reserve(size + 1); buffer_[size] = '\0'; @@ -422,7 +434,9 @@ class BasicFormatter { /** Returns the content of the output buffer as an `std::string`. */ - std::string str() const { return std::string(&buffer_[0], buffer_.size()); } + std::basic_string str() const { + return std::basic_string(&buffer_[0], buffer_.size()); + } BasicFormatter &operator<<(int value) { return *this << IntFormatter >(value, TypeSpec<0>()); @@ -431,12 +445,12 @@ class BasicFormatter { return *this << IntFormatter >(value, TypeSpec<0>()); } - BasicFormatter &operator<<(char value) { + BasicFormatter &operator<<(Char value) { *GrowBuffer(1) = value; return *this; } - BasicFormatter &operator<<(const char *value) { + BasicFormatter &operator<<(const Char *value) { std::size_t size = std::strlen(value); std::strncpy(GrowBuffer(size), value, size); return *this; @@ -445,7 +459,7 @@ class BasicFormatter { template BasicFormatter &operator<<(const IntFormatter &f); - void Write(const std::string &s, const FormatSpec &spec) { + void Write(const std::basic_string &s, const FormatSpec &spec) { FormatString(s.data(), s.size(), spec); } @@ -454,8 +468,249 @@ class BasicFormatter { } }; +// Fills the padding around the content and returns the pointer to the +// content area. +template +Char *BasicFormatter::FillPadding(Char *buffer, + unsigned total_size, std::size_t content_size, char fill) { + std::size_t padding = total_size - content_size; + std::size_t left_padding = padding / 2; + std::fill_n(buffer, left_padding, fill); + buffer += left_padding; + char *content = buffer; + std::fill_n(buffer + content_size, padding - left_padding, fill); + return content; +} + +template +void BasicFormatter::FormatDecimal( + Char *buffer, uint64_t value, unsigned num_digits) { + --num_digits; + while (value >= 100) { + // Integer division is slow so do it for a group of two digits instead + // of for every digit. The idea comes from the talk by Alexandrescu + // "Three Optimization Tips for C++". See speed-test for a comparison. + unsigned index = (value % 100) * 2; + value /= 100; + buffer[num_digits] = internal::DIGITS[index + 1]; + buffer[num_digits - 1] = internal::DIGITS[index]; + num_digits -= 2; + } + if (value < 10) { + *buffer = static_cast('0' + value); + return; + } + unsigned index = static_cast(value * 2); + buffer[1] = internal::DIGITS[index + 1]; + buffer[0] = internal::DIGITS[index]; +} + +template +Char *BasicFormatter::PrepareFilledBuffer( + unsigned size, const AlignSpec &spec, char sign) { + unsigned width = spec.width(); + if (width <= size) { + char *p = GrowBuffer(size); + *p = sign; + return p + size - 1; + } + char *p = GrowBuffer(width); + char *end = p + width; + Alignment align = spec.align(); + if (align == ALIGN_LEFT) { + *p = sign; + p += size; + std::fill(p, end, spec.fill()); + } else if (align == ALIGN_CENTER) { + p = FillPadding(p, width, size, spec.fill()); + *p = sign; + p += size; + } else { + if (align == ALIGN_NUMERIC) { + if (sign) { + *p++ = sign; + --size; + } + } else { + *(end - size) = sign; + } + std::fill(p, end - size, spec.fill()); + p = end; + } + return p - 1; +} + +template +template +void BasicFormatter::FormatDouble( + T value, const FormatSpec &spec, int precision) { + // Check type. + char type = spec.type(); + bool upper = false; + switch (type) { + case 0: + type = 'g'; + break; + case 'e': case 'f': case 'g': + break; + case 'F': +#ifdef _MSC_VER + // MSVC's printf doesn't support 'F'. + type = 'f'; +#endif + // Fall through. + case 'E': case 'G': + upper = true; + break; + default: + internal::ReportUnknownType(type, "double"); + break; + } + + char sign = 0; + // Use SignBit instead of value < 0 because the latter is always + // false for NaN. + if (SignBit(value)) { + sign = '-'; + value = -value; + } else if (spec.sign_flag()) { + sign = spec.plus_flag() ? '+' : ' '; + } + + if (value != value) { + // Format NaN ourselves because sprintf's output is not consistent + // across platforms. + std::size_t size = 4; + const char *nan = upper ? " NAN" : " nan"; + if (!sign) { + --size; + ++nan; + } + char *out = FormatString(nan, size, spec); + if (sign) + *out = sign; + return; + } + + if (isinf(value)) { + // Format infinity ourselves because sprintf's output is not consistent + // across platforms. + std::size_t size = 4; + const char *inf = upper ? " INF" : " inf"; + if (!sign) { + --size; + ++inf; + } + char *out = FormatString(inf, size, spec); + if (sign) + *out = sign; + return; + } + + size_t offset = buffer_.size(); + unsigned width = spec.width(); + if (sign) { + buffer_.reserve(buffer_.size() + std::max(width, 1u)); + if (width > 0) + --width; + ++offset; + } + + // Build format string. + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg + char format[MAX_FORMAT_SIZE]; + char *format_ptr = format; + *format_ptr++ = '%'; + unsigned width_for_sprintf = width; + if (spec.hash_flag()) + *format_ptr++ = '#'; + if (spec.align() == ALIGN_CENTER) { + width_for_sprintf = 0; + } else { + if (spec.align() == ALIGN_LEFT) + *format_ptr++ = '-'; + if (width != 0) + *format_ptr++ = '*'; + } + if (precision >= 0) { + *format_ptr++ = '.'; + *format_ptr++ = '*'; + } + if (internal::IsLongDouble::VALUE) + *format_ptr++ = 'L'; + *format_ptr++ = type; + *format_ptr = '\0'; + + // Format using snprintf. + for (;;) { + size_t size = buffer_.capacity() - offset; + int n = 0; + char *start = &buffer_[offset]; + if (width_for_sprintf == 0) { + n = precision < 0 ? + snprintf(start, size, format, value) : + snprintf(start, size, format, precision, value); + } else { + n = precision < 0 ? + snprintf(start, size, format, width_for_sprintf, value) : + snprintf(start, size, format, width_for_sprintf, precision, value); + } + if (n >= 0 && offset + n < buffer_.capacity()) { + if (sign) { + if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || + *start != ' ') { + *(start - 1) = sign; + sign = 0; + } else { + *(start - 1) = spec.fill(); + } + ++n; + } + if (spec.align() == ALIGN_CENTER && + spec.width() > static_cast(n)) { + char *p = GrowBuffer(spec.width()); + std::copy(p, p + n, p + (spec.width() - n) / 2); + FillPadding(p, spec.width(), n, spec.fill()); + return; + } + if (spec.fill() != ' ' || sign) { + while (*start == ' ') + *start++ = spec.fill(); + if (sign) + *(start - 1) = sign; + } + GrowBuffer(n); + return; + } + buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity()); + } +} + +template +char *BasicFormatter::FormatString( + const char *s, std::size_t size, const FormatSpec &spec) { + char *out = 0; + if (spec.width() > size) { + out = GrowBuffer(spec.width()); + if (spec.align() == ALIGN_RIGHT) { + std::fill_n(out, spec.width() - size, spec.fill()); + out += spec.width() - size; + } else if (spec.align() == ALIGN_CENTER) { + out = FillPadding(out, spec.width(), size, spec.fill()); + } else { + std::fill_n(out + size, spec.width() - size, spec.fill()); + } + } else { + out = GrowBuffer(size); + } + std::copy(s, s + size, out); + return out; +} + +template template -BasicFormatter &BasicFormatter::operator<<(const IntFormatter &f) { +BasicFormatter &BasicFormatter::operator<<( + const IntFormatter &f) { T value = f.value(); unsigned size = 0; char sign = 0; @@ -472,7 +727,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter &f) { switch (f.type()) { case 0: case 'd': { unsigned num_digits = BasicFormatter::CountDigits(abs_value); - char *p = PrepareFilledBuffer(size + num_digits, f, sign) - num_digits + 1; + Char *p = PrepareFilledBuffer(size + num_digits, f, sign) - num_digits + 1; BasicFormatter::FormatDecimal(p, abs_value, num_digits); break; } @@ -483,7 +738,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter &f) { do { ++size; } while ((n >>= 4) != 0); - char *p = PrepareFilledBuffer(size, f, sign); + Char *p = PrepareFilledBuffer(size, f, sign); n = abs_value; const char *digits = f.type() == 'x' ? "0123456789abcdef" : "0123456789ABCDEF"; @@ -503,7 +758,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter &f) { do { ++size; } while ((n >>= 3) != 0); - char *p = PrepareFilledBuffer(size, f, sign); + Char *p = PrepareFilledBuffer(size, f, sign); n = abs_value; do { *p-- = '0' + (n & 7); @@ -513,16 +768,16 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter &f) { break; } default: - BasicFormatter::ReportUnknownType(f.type(), "integer"); + internal::ReportUnknownType(f.type(), "integer"); break; } return *this; } // The default formatting function. -template -void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) { - std::ostringstream os; +template +void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) { + std::basic_ostringstream os; os << value; f.Write(os.str(), spec); } @@ -551,7 +806,7 @@ void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) { The buffer can be accessed using :meth:`data` or :meth:`c_str`. \endrst */ -class Formatter : public BasicFormatter { +class Formatter : public BasicFormatter { private: enum Type { // Numeric types should go first. @@ -704,8 +959,13 @@ class Formatter : public BasicFormatter { internal::ArgInserter operator()(StringRef format); }; -inline std::string str(const BasicFormatter &f) { return f.str(); } -inline const char *c_str(const BasicFormatter &f) { return f.c_str(); } +template +inline std::basic_string str(const BasicFormatter &f) { + return f.str(); +} + +template +inline const Char *c_str(const BasicFormatter &f) { return f.c_str(); } std::string str(internal::FormatterProxy p); const char *c_str(internal::FormatterProxy p); diff --git a/format_test.cc b/format_test.cc index 6fa570b4..6d0f55af 100644 --- a/format_test.cc +++ b/format_test.cc @@ -862,7 +862,9 @@ class Date { return os; } - friend BasicFormatter &operator<<(BasicFormatter &f, const Date &d) { + template + friend BasicFormatter &operator<<( + BasicFormatter &f, const Date &d) { return f << d.year_ << '-' << d.month_ << '-' << d.day_; } }; @@ -877,7 +879,8 @@ TEST(FormatterTest, FormatUsingIOStreams) { class Answer {}; -void Format(fmt::BasicFormatter &f, const fmt::FormatSpec &spec, Answer) { +template +void Format(fmt::BasicFormatter &f, const fmt::FormatSpec &spec, Answer) { f.Write("42", spec); } @@ -918,7 +921,7 @@ TEST(FormatterTest, FormatterAppend) { TEST(FormatterTest, FormatterExamples) { using fmt::hex; - EXPECT_EQ("0000cafe", str(BasicFormatter() << pad(hex(0xcafe), 8, '0'))); + EXPECT_EQ("0000cafe", str(BasicFormatter() << pad(hex(0xcafe), 8, '0'))); std::string message = str(Format("The answer is {}") << 42); EXPECT_EQ("The answer is 42", message); @@ -1068,11 +1071,11 @@ TEST(TempFormatterTest, Examples) { TEST(StrTest, oct) { using fmt::oct; - EXPECT_EQ("12", (BasicFormatter() << oct(static_cast(012))).str()); - EXPECT_EQ("12", (BasicFormatter() << oct(012)).str()); - EXPECT_EQ("34", (BasicFormatter() << oct(034u)).str()); - EXPECT_EQ("56", (BasicFormatter() << oct(056l)).str()); - EXPECT_EQ("70", (BasicFormatter() << oct(070ul)).str()); + EXPECT_EQ("12", str(BasicFormatter() << oct(static_cast(012)))); + EXPECT_EQ("12", str(BasicFormatter() << oct(012))); + EXPECT_EQ("34", str(BasicFormatter() << oct(034u))); + EXPECT_EQ("56", str(BasicFormatter() << oct(056l))); + EXPECT_EQ("70", str(BasicFormatter() << oct(070ul))); } TEST(StrTest, hex) { @@ -1082,17 +1085,17 @@ TEST(StrTest, hex) { // This shouldn't compile: //fmt::IntFormatter > (*phex2)(short value) = hex; - EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str()); - EXPECT_EQ("babe", (BasicFormatter() << hex(0xbabeu)).str()); - EXPECT_EQ("dead", (BasicFormatter() << hex(0xdeadl)).str()); - EXPECT_EQ("beef", (BasicFormatter() << hex(0xbeeful)).str()); + EXPECT_EQ("cafe", str(BasicFormatter() << hex(0xcafe))); + EXPECT_EQ("babe", str(BasicFormatter() << hex(0xbabeu))); + EXPECT_EQ("dead", str(BasicFormatter() << hex(0xdeadl))); + EXPECT_EQ("beef", str(BasicFormatter() << hex(0xbeeful))); } TEST(StrTest, hexu) { - EXPECT_EQ("CAFE", (BasicFormatter() << hexu(0xcafe)).str()); - EXPECT_EQ("BABE", (BasicFormatter() << hexu(0xbabeu)).str()); - EXPECT_EQ("DEAD", (BasicFormatter() << hexu(0xdeadl)).str()); - EXPECT_EQ("BEEF", (BasicFormatter() << hexu(0xbeeful)).str()); + EXPECT_EQ("CAFE", str(BasicFormatter() << hexu(0xcafe))); + EXPECT_EQ("BABE", str(BasicFormatter() << hexu(0xbabeu))); + EXPECT_EQ("DEAD", str(BasicFormatter() << hexu(0xdeadl))); + EXPECT_EQ("BEEF", str(BasicFormatter() << hexu(0xbeeful))); } class ISO8601DateFormatter { @@ -1101,8 +1104,9 @@ class ISO8601DateFormatter { public: ISO8601DateFormatter(const Date &d) : date_(&d) {} - friend BasicFormatter &operator<<( - BasicFormatter &f, const ISO8601DateFormatter &d) { + template + friend BasicFormatter &operator<<( + BasicFormatter &f, const ISO8601DateFormatter &d) { return f << pad(d.date_->year(), 4, '0') << '-' << pad(d.date_->month(), 2, '0') << '-' << pad(d.date_->day(), 2, '0'); } @@ -1112,17 +1116,17 @@ ISO8601DateFormatter iso8601(const Date &d) { return ISO8601DateFormatter(d); } TEST(StrTest, pad) { using fmt::hex; - EXPECT_EQ(" cafe", (BasicFormatter() << pad(hex(0xcafe), 8)).str()); - EXPECT_EQ(" babe", (BasicFormatter() << pad(hex(0xbabeu), 8)).str()); - EXPECT_EQ(" dead", (BasicFormatter() << pad(hex(0xdeadl), 8)).str()); - EXPECT_EQ(" beef", (BasicFormatter() << pad(hex(0xbeeful), 8)).str()); + EXPECT_EQ(" cafe", str(BasicFormatter() << pad(hex(0xcafe), 8))); + EXPECT_EQ(" babe", str(BasicFormatter() << pad(hex(0xbabeu), 8))); + EXPECT_EQ(" dead", str(BasicFormatter() << pad(hex(0xdeadl), 8))); + EXPECT_EQ(" beef", str(BasicFormatter() << pad(hex(0xbeeful), 8))); - EXPECT_EQ(" 11", (BasicFormatter() << pad(11, 7)).str()); - EXPECT_EQ(" 22", (BasicFormatter() << pad(22u, 7)).str()); - EXPECT_EQ(" 33", (BasicFormatter() << pad(33l, 7)).str()); - EXPECT_EQ(" 44", (BasicFormatter() << pad(44lu, 7)).str()); + EXPECT_EQ(" 11", str(BasicFormatter() << pad(11, 7))); + EXPECT_EQ(" 22", str(BasicFormatter() << pad(22u, 7))); + EXPECT_EQ(" 33", str(BasicFormatter() << pad(33l, 7))); + EXPECT_EQ(" 44", str(BasicFormatter() << pad(44lu, 7))); - BasicFormatter f; + BasicFormatter f; f.Clear(); f << pad(42, 5, '0'); EXPECT_EQ("00042", f.str()); @@ -1137,8 +1141,12 @@ TEST(StrTest, pad) { TEST(StrTest, NoConflictWithIOManip) { using namespace std; using namespace fmt; - EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str()); - EXPECT_EQ("12", (BasicFormatter() << oct(012)).str()); + EXPECT_EQ("cafe", str(BasicFormatter() << hex(0xcafe))); + EXPECT_EQ("12", str(BasicFormatter() << oct(012))); +} + +TEST(StrTest, BasicFormatterWChar) { + EXPECT_EQ(L"cafe", str(BasicFormatter() << fmt::hex(0xcafe))); } template