Parameterize BasicFormatter on char type.

This commit is contained in:
Victor Zverovich 2013-01-26 16:07:28 -08:00
parent dbfd021ae2
commit 687301c516
3 changed files with 334 additions and 306 deletions

266
format.cc
View File

@ -64,32 +64,6 @@ using fmt::StringRef;
namespace { namespace {
template <typename T>
struct IsLongDouble { enum {VALUE = 0}; };
template <>
struct IsLongDouble<long double> { 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 #ifdef _MSC_VER
int SignBit(double value) { int SignBit(double value) {
if (value < 0) return 1; if (value < 0) return 1;
@ -101,237 +75,23 @@ int SignBit(double value) {
#endif #endif
} }
void BasicFormatter::FormatDecimal( const char fmt::internal::DIGITS[] =
char *buffer, uint64_t value, unsigned num_digits) { "0001020304050607080910111213141516171819"
--num_digits; "2021222324252627282930313233343536373839"
while (value >= 100) { "4041424344454647484950515253545556575859"
// Integer division is slow so do it for a group of two digits instead "6061626364656667686970717273747576777879"
// of for every digit. The idea comes from the talk by Alexandrescu "8081828384858687888990919293949596979899";
// "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<char>('0' + value);
return;
}
unsigned index = static_cast<unsigned>(value * 2);
buffer[1] = DIGITS[index + 1];
buffer[0] = DIGITS[index];
}
void BasicFormatter::ReportUnknownType(char code, const char *type) { void fmt::internal::ReportUnknownType(char code, const char *type) {
if (std::isprint(static_cast<unsigned char>(code))) { if (std::isprint(static_cast<unsigned char>(code))) {
throw fmt::FormatError(fmt::str( 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( 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<unsigned>(code) << type)); << static_cast<unsigned>(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 <typename T>
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<T>::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<unsigned>(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 // Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{' // FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors. // should override other errors.
@ -573,7 +333,7 @@ void Formatter::DoFormat() {
break; break;
case CHAR: { case CHAR: {
if (spec.type_ && spec.type_ != 'c') if (spec.type_ && spec.type_ != 'c')
ReportUnknownType(spec.type_, "char"); internal::ReportUnknownType(spec.type_, "char");
char *out = 0; char *out = 0;
if (spec.width_ > 1) { if (spec.width_ > 1) {
out = GrowBuffer(spec.width_); out = GrowBuffer(spec.width_);
@ -593,7 +353,7 @@ void Formatter::DoFormat() {
} }
case STRING: { case STRING: {
if (spec.type_ && spec.type_ != 's') if (spec.type_ && spec.type_ != 's')
ReportUnknownType(spec.type_, "string"); internal::ReportUnknownType(spec.type_, "string");
const char *str = arg.string.value; const char *str = arg.string.value;
size_t size = arg.string.size; size_t size = arg.string.size;
if (size == 0) { if (size == 0) {
@ -607,14 +367,14 @@ void Formatter::DoFormat() {
} }
case POINTER: case POINTER:
if (spec.type_ && spec.type_ != 'p') if (spec.type_ && spec.type_ != 'p')
ReportUnknownType(spec.type_, "pointer"); internal::ReportUnknownType(spec.type_, "pointer");
spec.flags_= HASH_FLAG; spec.flags_= HASH_FLAG;
spec.type_ = 'x'; spec.type_ = 'x';
FormatInt(reinterpret_cast<uintptr_t>(arg.pointer_value), spec); FormatInt(reinterpret_cast<uintptr_t>(arg.pointer_value), spec);
break; break;
case CUSTOM: case CUSTOM:
if (spec.type_) if (spec.type_)
ReportUnknownType(spec.type_, "object"); internal::ReportUnknownType(spec.type_, "object");
(this->*arg.custom.format)(arg.custom.value, spec); (this->*arg.custom.format)(arg.custom.value, spec);
break; break;
default: default:

308
format.h
View File

@ -136,6 +136,16 @@ struct IntTraits<int> : SignedIntTraits<int, unsigned> {};
template <> template <>
struct IntTraits<long> : SignedIntTraits<long, unsigned long> {}; struct IntTraits<long> : SignedIntTraits<long, unsigned long> {};
template <typename T>
struct IsLongDouble { enum {VALUE = 0}; };
template <>
struct IsLongDouble<long double> { enum {VALUE = 1}; };
extern const char DIGITS[];
void ReportUnknownType(char code, const char *type);
class ArgInserter; class ArgInserter;
class FormatterProxy; class FormatterProxy;
} }
@ -341,6 +351,7 @@ DEFINE_INT_FORMATTERS(long)
DEFINE_INT_FORMATTERS(unsigned) DEFINE_INT_FORMATTERS(unsigned)
DEFINE_INT_FORMATTERS(unsigned long) DEFINE_INT_FORMATTERS(unsigned long)
template <typename Char>
class BasicFormatter { class BasicFormatter {
private: private:
// Returns the number of decimal digits in n. Trailing zeros are not counted // 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: 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 }; enum { INLINE_BUFFER_SIZE = 500 };
mutable internal::Array<char, INLINE_BUFFER_SIZE> buffer_; // Output buffer. mutable internal::Array<Char, INLINE_BUFFER_SIZE> buffer_; // Output buffer.
// Grows the buffer by n characters and returns a pointer to the newly // Grows the buffer by n characters and returns a pointer to the newly
// allocated area. // allocated area.
char *GrowBuffer(std::size_t n) { Char *GrowBuffer(std::size_t n) {
std::size_t size = buffer_.size(); std::size_t size = buffer_.size();
buffer_.resize(size + n); buffer_.resize(size + n);
return &buffer_[size]; return &buffer_[size];
} }
char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) { Char *PrepareFilledBuffer(unsigned size, const Spec &, char sign) {
char *p = GrowBuffer(size); Char *p = GrowBuffer(size);
*p = sign; *p = sign;
return p + size - 1; 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. // Formats an integer.
template <typename T> template <typename T>
@ -406,13 +418,13 @@ class BasicFormatter {
Returns a pointer to the output buffer content. No terminating null Returns a pointer to the output buffer content. No terminating null
character is appended. 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 Returns a pointer to the output buffer content with terminating null
character appended. character appended.
*/ */
const char *c_str() const { const Char *c_str() const {
std::size_t size = buffer_.size(); std::size_t size = buffer_.size();
buffer_.reserve(size + 1); buffer_.reserve(size + 1);
buffer_[size] = '\0'; buffer_[size] = '\0';
@ -422,7 +434,9 @@ class BasicFormatter {
/** /**
Returns the content of the output buffer as an `std::string`. 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<Char> str() const {
return std::basic_string<Char>(&buffer_[0], buffer_.size());
}
BasicFormatter &operator<<(int value) { BasicFormatter &operator<<(int value) {
return *this << IntFormatter<int, TypeSpec<0> >(value, TypeSpec<0>()); return *this << IntFormatter<int, TypeSpec<0> >(value, TypeSpec<0>());
@ -431,12 +445,12 @@ class BasicFormatter {
return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>()); return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>());
} }
BasicFormatter &operator<<(char value) { BasicFormatter &operator<<(Char value) {
*GrowBuffer(1) = value; *GrowBuffer(1) = value;
return *this; return *this;
} }
BasicFormatter &operator<<(const char *value) { BasicFormatter &operator<<(const Char *value) {
std::size_t size = std::strlen(value); std::size_t size = std::strlen(value);
std::strncpy(GrowBuffer(size), value, size); std::strncpy(GrowBuffer(size), value, size);
return *this; return *this;
@ -445,7 +459,7 @@ class BasicFormatter {
template <typename T, typename Spec> template <typename T, typename Spec>
BasicFormatter &operator<<(const IntFormatter<T, Spec> &f); BasicFormatter &operator<<(const IntFormatter<T, Spec> &f);
void Write(const std::string &s, const FormatSpec &spec) { void Write(const std::basic_string<char> &s, const FormatSpec &spec) {
FormatString(s.data(), s.size(), 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 <typename Char>
Char *BasicFormatter<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;
}
template <typename Char>
void BasicFormatter<Char>::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<char>('0' + value);
return;
}
unsigned index = static_cast<unsigned>(value * 2);
buffer[1] = internal::DIGITS[index + 1];
buffer[0] = internal::DIGITS[index];
}
template <typename Char>
Char *BasicFormatter<Char>::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 <typename Char>
template <typename T>
void BasicFormatter<Char>::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<T>::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<unsigned>(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 <typename Char>
char *BasicFormatter<Char>::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 <typename Char>
template <typename T, typename Spec> template <typename T, typename Spec>
BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) { BasicFormatter<Char> &BasicFormatter<Char>::operator<<(
const IntFormatter<T, Spec> &f) {
T value = f.value(); T value = f.value();
unsigned size = 0; unsigned size = 0;
char sign = 0; char sign = 0;
@ -472,7 +727,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
switch (f.type()) { switch (f.type()) {
case 0: case 'd': { case 0: case 'd': {
unsigned num_digits = BasicFormatter::CountDigits(abs_value); 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); BasicFormatter::FormatDecimal(p, abs_value, num_digits);
break; break;
} }
@ -483,7 +738,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
do { do {
++size; ++size;
} while ((n >>= 4) != 0); } while ((n >>= 4) != 0);
char *p = PrepareFilledBuffer(size, f, sign); Char *p = PrepareFilledBuffer(size, f, sign);
n = abs_value; n = abs_value;
const char *digits = f.type() == 'x' ? const char *digits = f.type() == 'x' ?
"0123456789abcdef" : "0123456789ABCDEF"; "0123456789abcdef" : "0123456789ABCDEF";
@ -503,7 +758,7 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
do { do {
++size; ++size;
} while ((n >>= 3) != 0); } while ((n >>= 3) != 0);
char *p = PrepareFilledBuffer(size, f, sign); Char *p = PrepareFilledBuffer(size, f, sign);
n = abs_value; n = abs_value;
do { do {
*p-- = '0' + (n & 7); *p-- = '0' + (n & 7);
@ -513,16 +768,16 @@ BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
break; break;
} }
default: default:
BasicFormatter::ReportUnknownType(f.type(), "integer"); internal::ReportUnknownType(f.type(), "integer");
break; break;
} }
return *this; return *this;
} }
// The default formatting function. // The default formatting function.
template <typename T> template <typename Char, typename T>
void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) { void Format(BasicFormatter<Char> &f, const FormatSpec &spec, const T &value) {
std::ostringstream os; std::basic_ostringstream<Char> os;
os << value; os << value;
f.Write(os.str(), spec); 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`. The buffer can be accessed using :meth:`data` or :meth:`c_str`.
\endrst \endrst
*/ */
class Formatter : public BasicFormatter { class Formatter : public BasicFormatter<char> {
private: private:
enum Type { enum Type {
// Numeric types should go first. // Numeric types should go first.
@ -704,8 +959,13 @@ class Formatter : public BasicFormatter {
internal::ArgInserter operator()(StringRef format); internal::ArgInserter operator()(StringRef format);
}; };
inline std::string str(const BasicFormatter &f) { return f.str(); } template <typename Char>
inline const char *c_str(const BasicFormatter &f) { return f.c_str(); } inline std::basic_string<Char> str(const BasicFormatter<Char> &f) {
return f.str();
}
template <typename Char>
inline const Char *c_str(const BasicFormatter<Char> &f) { return f.c_str(); }
std::string str(internal::FormatterProxy p); std::string str(internal::FormatterProxy p);
const char *c_str(internal::FormatterProxy p); const char *c_str(internal::FormatterProxy p);

View File

@ -862,7 +862,9 @@ class Date {
return os; return os;
} }
friend BasicFormatter &operator<<(BasicFormatter &f, const Date &d) { template <typename Char>
friend BasicFormatter<Char> &operator<<(
BasicFormatter<Char> &f, const Date &d) {
return f << d.year_ << '-' << d.month_ << '-' << d.day_; return f << d.year_ << '-' << d.month_ << '-' << d.day_;
} }
}; };
@ -877,7 +879,8 @@ TEST(FormatterTest, FormatUsingIOStreams) {
class Answer {}; class Answer {};
void Format(fmt::BasicFormatter &f, const fmt::FormatSpec &spec, Answer) { template <typename Char>
void Format(fmt::BasicFormatter<Char> &f, const fmt::FormatSpec &spec, Answer) {
f.Write("42", spec); f.Write("42", spec);
} }
@ -918,7 +921,7 @@ TEST(FormatterTest, FormatterAppend) {
TEST(FormatterTest, FormatterExamples) { TEST(FormatterTest, FormatterExamples) {
using fmt::hex; using fmt::hex;
EXPECT_EQ("0000cafe", str(BasicFormatter() << pad(hex(0xcafe), 8, '0'))); EXPECT_EQ("0000cafe", str(BasicFormatter<char>() << pad(hex(0xcafe), 8, '0')));
std::string message = str(Format("The answer is {}") << 42); std::string message = str(Format("The answer is {}") << 42);
EXPECT_EQ("The answer is 42", message); EXPECT_EQ("The answer is 42", message);
@ -1068,11 +1071,11 @@ TEST(TempFormatterTest, Examples) {
TEST(StrTest, oct) { TEST(StrTest, oct) {
using fmt::oct; using fmt::oct;
EXPECT_EQ("12", (BasicFormatter() << oct(static_cast<short>(012))).str()); EXPECT_EQ("12", str(BasicFormatter<char>() << oct(static_cast<short>(012))));
EXPECT_EQ("12", (BasicFormatter() << oct(012)).str()); EXPECT_EQ("12", str(BasicFormatter<char>() << oct(012)));
EXPECT_EQ("34", (BasicFormatter() << oct(034u)).str()); EXPECT_EQ("34", str(BasicFormatter<char>() << oct(034u)));
EXPECT_EQ("56", (BasicFormatter() << oct(056l)).str()); EXPECT_EQ("56", str(BasicFormatter<char>() << oct(056l)));
EXPECT_EQ("70", (BasicFormatter() << oct(070ul)).str()); EXPECT_EQ("70", str(BasicFormatter<char>() << oct(070ul)));
} }
TEST(StrTest, hex) { TEST(StrTest, hex) {
@ -1082,17 +1085,17 @@ TEST(StrTest, hex) {
// This shouldn't compile: // This shouldn't compile:
//fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex; //fmt::IntFormatter<short, fmt::TypeSpec<'x'> > (*phex2)(short value) = hex;
EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str()); EXPECT_EQ("cafe", str(BasicFormatter<char>() << hex(0xcafe)));
EXPECT_EQ("babe", (BasicFormatter() << hex(0xbabeu)).str()); EXPECT_EQ("babe", str(BasicFormatter<char>() << hex(0xbabeu)));
EXPECT_EQ("dead", (BasicFormatter() << hex(0xdeadl)).str()); EXPECT_EQ("dead", str(BasicFormatter<char>() << hex(0xdeadl)));
EXPECT_EQ("beef", (BasicFormatter() << hex(0xbeeful)).str()); EXPECT_EQ("beef", str(BasicFormatter<char>() << hex(0xbeeful)));
} }
TEST(StrTest, hexu) { TEST(StrTest, hexu) {
EXPECT_EQ("CAFE", (BasicFormatter() << hexu(0xcafe)).str()); EXPECT_EQ("CAFE", str(BasicFormatter<char>() << hexu(0xcafe)));
EXPECT_EQ("BABE", (BasicFormatter() << hexu(0xbabeu)).str()); EXPECT_EQ("BABE", str(BasicFormatter<char>() << hexu(0xbabeu)));
EXPECT_EQ("DEAD", (BasicFormatter() << hexu(0xdeadl)).str()); EXPECT_EQ("DEAD", str(BasicFormatter<char>() << hexu(0xdeadl)));
EXPECT_EQ("BEEF", (BasicFormatter() << hexu(0xbeeful)).str()); EXPECT_EQ("BEEF", str(BasicFormatter<char>() << hexu(0xbeeful)));
} }
class ISO8601DateFormatter { class ISO8601DateFormatter {
@ -1101,8 +1104,9 @@ class ISO8601DateFormatter {
public: public:
ISO8601DateFormatter(const Date &d) : date_(&d) {} ISO8601DateFormatter(const Date &d) : date_(&d) {}
friend BasicFormatter &operator<<( template <typename Char>
BasicFormatter &f, const ISO8601DateFormatter &d) { friend BasicFormatter<Char> &operator<<(
BasicFormatter<Char> &f, const ISO8601DateFormatter &d) {
return f << pad(d.date_->year(), 4, '0') << '-' return f << pad(d.date_->year(), 4, '0') << '-'
<< pad(d.date_->month(), 2, '0') << '-' << pad(d.date_->day(), 2, '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) { TEST(StrTest, pad) {
using fmt::hex; using fmt::hex;
EXPECT_EQ(" cafe", (BasicFormatter() << pad(hex(0xcafe), 8)).str()); EXPECT_EQ(" cafe", str(BasicFormatter<char>() << pad(hex(0xcafe), 8)));
EXPECT_EQ(" babe", (BasicFormatter() << pad(hex(0xbabeu), 8)).str()); EXPECT_EQ(" babe", str(BasicFormatter<char>() << pad(hex(0xbabeu), 8)));
EXPECT_EQ(" dead", (BasicFormatter() << pad(hex(0xdeadl), 8)).str()); EXPECT_EQ(" dead", str(BasicFormatter<char>() << pad(hex(0xdeadl), 8)));
EXPECT_EQ(" beef", (BasicFormatter() << pad(hex(0xbeeful), 8)).str()); EXPECT_EQ(" beef", str(BasicFormatter<char>() << pad(hex(0xbeeful), 8)));
EXPECT_EQ(" 11", (BasicFormatter() << pad(11, 7)).str()); EXPECT_EQ(" 11", str(BasicFormatter<char>() << pad(11, 7)));
EXPECT_EQ(" 22", (BasicFormatter() << pad(22u, 7)).str()); EXPECT_EQ(" 22", str(BasicFormatter<char>() << pad(22u, 7)));
EXPECT_EQ(" 33", (BasicFormatter() << pad(33l, 7)).str()); EXPECT_EQ(" 33", str(BasicFormatter<char>() << pad(33l, 7)));
EXPECT_EQ(" 44", (BasicFormatter() << pad(44lu, 7)).str()); EXPECT_EQ(" 44", str(BasicFormatter<char>() << pad(44lu, 7)));
BasicFormatter f; BasicFormatter<char> f;
f.Clear(); f.Clear();
f << pad(42, 5, '0'); f << pad(42, 5, '0');
EXPECT_EQ("00042", f.str()); EXPECT_EQ("00042", f.str());
@ -1137,8 +1141,12 @@ TEST(StrTest, pad) {
TEST(StrTest, NoConflictWithIOManip) { TEST(StrTest, NoConflictWithIOManip) {
using namespace std; using namespace std;
using namespace fmt; using namespace fmt;
EXPECT_EQ("cafe", (BasicFormatter() << hex(0xcafe)).str()); EXPECT_EQ("cafe", str(BasicFormatter<char>() << hex(0xcafe)));
EXPECT_EQ("12", (BasicFormatter() << oct(012)).str()); EXPECT_EQ("12", str(BasicFormatter<char>() << oct(012)));
}
TEST(StrTest, BasicFormatterWChar) {
EXPECT_EQ(L"cafe", str(BasicFormatter<wchar_t>() << fmt::hex(0xcafe)));
} }
template <typename T> template <typename T>