forked from fmtlib/fmt
Parameterize integer formatting method on format spec type. Add Sprint/iomanip style formatting methods (oct, hex, hexu, pad).
This commit is contained in:
318
format.cc
318
format.cc
@@ -34,7 +34,6 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -44,8 +43,10 @@
|
|||||||
|
|
||||||
using std::size_t;
|
using std::size_t;
|
||||||
using fmt::BasicFormatter;
|
using fmt::BasicFormatter;
|
||||||
|
using fmt::IntFormatter;
|
||||||
using fmt::Formatter;
|
using fmt::Formatter;
|
||||||
using fmt::FormatSpec;
|
using fmt::FormatSpec;
|
||||||
|
using fmt::WidthSpec;
|
||||||
using fmt::StringRef;
|
using fmt::StringRef;
|
||||||
|
|
||||||
#if _MSC_VER
|
#if _MSC_VER
|
||||||
@@ -56,59 +57,12 @@ using fmt::StringRef;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Flags.
|
|
||||||
enum { SIGN_FLAG = 1, PLUS_FLAG = 2, HASH_FLAG = 4 };
|
|
||||||
|
|
||||||
void ReportUnknownType(char code, const char *type) {
|
|
||||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
|
||||||
throw fmt::FormatError(
|
|
||||||
str(fmt::Format("unknown format code '{0}' for {1}") << code << type));
|
|
||||||
}
|
|
||||||
throw fmt::FormatError(
|
|
||||||
str(fmt::Format("unknown format code '\\x{0:02x}' for {1}")
|
|
||||||
<< static_cast<unsigned>(code) << type));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Information about an integer type.
|
|
||||||
template <typename T>
|
|
||||||
struct IntTraits {
|
|
||||||
typedef T UnsignedType;
|
|
||||||
static bool IsNegative(T) { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct IntTraits<int> {
|
|
||||||
typedef unsigned UnsignedType;
|
|
||||||
static bool IsNegative(int value) { return value < 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct IntTraits<long> {
|
|
||||||
typedef unsigned long UnsignedType;
|
|
||||||
static bool IsNegative(long value) { return value < 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct IsLongDouble { enum {VALUE = 0}; };
|
struct IsLongDouble { enum {VALUE = 0}; };
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct IsLongDouble<long double> { enum {VALUE = 1}; };
|
struct IsLongDouble<long double> { enum {VALUE = 1}; };
|
||||||
|
|
||||||
inline unsigned CountDigits(uint64_t n) {
|
|
||||||
unsigned count = 1;
|
|
||||||
for (;;) {
|
|
||||||
// Integer division is slow so do it for a group of four 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.
|
|
||||||
if (n < 10) return count;
|
|
||||||
if (n < 100) return count + 1;
|
|
||||||
if (n < 1000) return count + 2;
|
|
||||||
if (n < 10000) return count + 3;
|
|
||||||
n /= 10000u;
|
|
||||||
count += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char DIGITS[] =
|
const char DIGITS[] =
|
||||||
"0001020304050607080910111213141516171819"
|
"0001020304050607080910111213141516171819"
|
||||||
"2021222324252627282930313233343536373839"
|
"2021222324252627282930313233343536373839"
|
||||||
@@ -116,27 +70,6 @@ const char DIGITS[] =
|
|||||||
"6061626364656667686970717273747576777879"
|
"6061626364656667686970717273747576777879"
|
||||||
"8081828384858687888990919293949596979899";
|
"8081828384858687888990919293949596979899";
|
||||||
|
|
||||||
void 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<char>('0' + value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unsigned index = static_cast<unsigned>(value * 2);
|
|
||||||
buffer[1] = DIGITS[index + 1];
|
|
||||||
buffer[0] = DIGITS[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fills the padding around the content and returns the pointer to the
|
// Fills the padding around the content and returns the pointer to the
|
||||||
// content area.
|
// content area.
|
||||||
char *FillPadding(char *buffer,
|
char *FillPadding(char *buffer,
|
||||||
@@ -161,25 +94,59 @@ int signbit(double value) {
|
|||||||
#endif
|
#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<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) {
|
||||||
|
if (std::isprint(static_cast<unsigned char>(code))) {
|
||||||
|
throw fmt::FormatError(fmt::str(
|
||||||
|
fmt::Format("unknown format code '{0}' for {1}") << code << type));
|
||||||
|
}
|
||||||
|
throw fmt::FormatError(
|
||||||
|
fmt::str(fmt::Format("unknown format code '\\x{0:02x}' for {1}")
|
||||||
|
<< static_cast<unsigned>(code) << type));
|
||||||
|
}
|
||||||
|
|
||||||
char *BasicFormatter::PrepareFilledBuffer(
|
char *BasicFormatter::PrepareFilledBuffer(
|
||||||
unsigned size, const FormatSpec &spec, char sign) {
|
unsigned size, const AlignSpec &spec, char sign) {
|
||||||
if (spec.width <= size) {
|
unsigned width = spec.width();
|
||||||
|
if (width <= size) {
|
||||||
char *p = GrowBuffer(size);
|
char *p = GrowBuffer(size);
|
||||||
*p = sign;
|
*p = sign;
|
||||||
return p + size - 1;
|
return p + size - 1;
|
||||||
}
|
}
|
||||||
char *p = GrowBuffer(spec.width);
|
char *p = GrowBuffer(width);
|
||||||
char *end = p + spec.width;
|
char *end = p + width;
|
||||||
if (spec.align == ALIGN_LEFT) {
|
Alignment align = spec.align();
|
||||||
|
if (align == ALIGN_LEFT) {
|
||||||
*p = sign;
|
*p = sign;
|
||||||
p += size;
|
p += size;
|
||||||
std::fill(p, end, spec.fill);
|
std::fill(p, end, spec.fill());
|
||||||
} else if (spec.align == ALIGN_CENTER) {
|
} else if (align == ALIGN_CENTER) {
|
||||||
p = FillPadding(p, spec.width, size, spec.fill);
|
p = FillPadding(p, width, size, spec.fill());
|
||||||
*p = sign;
|
*p = sign;
|
||||||
p += size;
|
p += size;
|
||||||
} else {
|
} else {
|
||||||
if (spec.align == ALIGN_NUMERIC) {
|
if (align == ALIGN_NUMERIC) {
|
||||||
if (sign) {
|
if (sign) {
|
||||||
*p++ = sign;
|
*p++ = sign;
|
||||||
--size;
|
--size;
|
||||||
@@ -187,81 +154,17 @@ char *BasicFormatter::PrepareFilledBuffer(
|
|||||||
} else {
|
} else {
|
||||||
*(end - size) = sign;
|
*(end - size) = sign;
|
||||||
}
|
}
|
||||||
std::fill(p, end - size, spec.fill);
|
std::fill(p, end - size, spec.fill());
|
||||||
p = end;
|
p = end;
|
||||||
}
|
}
|
||||||
return p - 1;
|
return p - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void BasicFormatter::FormatInt(T value, const FormatSpec &spec) {
|
|
||||||
unsigned size = 0;
|
|
||||||
char sign = 0;
|
|
||||||
typedef typename IntTraits<T>::UnsignedType UnsignedType;
|
|
||||||
UnsignedType abs_value = value;
|
|
||||||
if (IntTraits<T>::IsNegative(value)) {
|
|
||||||
sign = '-';
|
|
||||||
++size;
|
|
||||||
abs_value = 0 - abs_value;
|
|
||||||
} else if ((spec.flags & SIGN_FLAG) != 0) {
|
|
||||||
sign = (spec.flags & PLUS_FLAG) != 0 ? '+' : ' ';
|
|
||||||
++size;
|
|
||||||
}
|
|
||||||
switch (spec.type) {
|
|
||||||
case 0: case 'd': {
|
|
||||||
unsigned num_digits = CountDigits(abs_value);
|
|
||||||
char *p = PrepareFilledBuffer(size + num_digits, spec, sign)
|
|
||||||
- num_digits + 1;
|
|
||||||
FormatDecimal(p, abs_value, num_digits);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'x': case 'X': {
|
|
||||||
UnsignedType n = abs_value;
|
|
||||||
bool print_prefix = (spec.flags & HASH_FLAG) != 0;
|
|
||||||
if (print_prefix) size += 2;
|
|
||||||
do {
|
|
||||||
++size;
|
|
||||||
} while ((n >>= 4) != 0);
|
|
||||||
char *p = PrepareFilledBuffer(size, spec, sign);
|
|
||||||
n = abs_value;
|
|
||||||
const char *digits = spec.type == 'x' ?
|
|
||||||
"0123456789abcdef" : "0123456789ABCDEF";
|
|
||||||
do {
|
|
||||||
*p-- = digits[n & 0xf];
|
|
||||||
} while ((n >>= 4) != 0);
|
|
||||||
if (print_prefix) {
|
|
||||||
*p-- = spec.type;
|
|
||||||
*p = '0';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'o': {
|
|
||||||
UnsignedType n = abs_value;
|
|
||||||
bool print_prefix = (spec.flags & HASH_FLAG) != 0;
|
|
||||||
if (print_prefix) ++size;
|
|
||||||
do {
|
|
||||||
++size;
|
|
||||||
} while ((n >>= 3) != 0);
|
|
||||||
char *p = PrepareFilledBuffer(size, spec, sign);
|
|
||||||
n = abs_value;
|
|
||||||
do {
|
|
||||||
*p-- = '0' + (n & 7);
|
|
||||||
} while ((n >>= 3) != 0);
|
|
||||||
if (print_prefix)
|
|
||||||
*p = '0';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ReportUnknownType(spec.type, "integer");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void BasicFormatter::FormatDouble(
|
void BasicFormatter::FormatDouble(
|
||||||
T value, const FormatSpec &spec, int precision) {
|
T value, const FormatSpec &spec, int precision) {
|
||||||
// Check type.
|
// Check type.
|
||||||
char type = spec.type;
|
char type = spec.type();
|
||||||
bool upper = false;
|
bool upper = false;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -289,8 +192,8 @@ void BasicFormatter::FormatDouble(
|
|||||||
if (signbit(value)) {
|
if (signbit(value)) {
|
||||||
sign = '-';
|
sign = '-';
|
||||||
value = -value;
|
value = -value;
|
||||||
} else if ((spec.flags & SIGN_FLAG) != 0) {
|
} else if (spec.sign_flag()) {
|
||||||
sign = (spec.flags & PLUS_FLAG) != 0 ? '+' : ' ';
|
sign = spec.plus_flag() ? '+' : ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value != value) {
|
if (value != value) {
|
||||||
@@ -324,7 +227,7 @@ void BasicFormatter::FormatDouble(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t offset = buffer_.size();
|
size_t offset = buffer_.size();
|
||||||
unsigned width = spec.width;
|
unsigned width = spec.width();
|
||||||
if (sign) {
|
if (sign) {
|
||||||
buffer_.reserve(buffer_.size() + std::max(width, 1u));
|
buffer_.reserve(buffer_.size() + std::max(width, 1u));
|
||||||
if (width > 0)
|
if (width > 0)
|
||||||
@@ -338,12 +241,12 @@ void BasicFormatter::FormatDouble(
|
|||||||
char *format_ptr = format;
|
char *format_ptr = format;
|
||||||
*format_ptr++ = '%';
|
*format_ptr++ = '%';
|
||||||
unsigned width_for_sprintf = width;
|
unsigned width_for_sprintf = width;
|
||||||
if ((spec.flags & HASH_FLAG) != 0)
|
if (spec.hash_flag())
|
||||||
*format_ptr++ = '#';
|
*format_ptr++ = '#';
|
||||||
if (spec.align == ALIGN_CENTER) {
|
if (spec.align() == ALIGN_CENTER) {
|
||||||
width_for_sprintf = 0;
|
width_for_sprintf = 0;
|
||||||
} else {
|
} else {
|
||||||
if (spec.align == ALIGN_LEFT)
|
if (spec.align() == ALIGN_LEFT)
|
||||||
*format_ptr++ = '-';
|
*format_ptr++ = '-';
|
||||||
if (width != 0)
|
if (width != 0)
|
||||||
*format_ptr++ = '*';
|
*format_ptr++ = '*';
|
||||||
@@ -373,24 +276,25 @@ void BasicFormatter::FormatDouble(
|
|||||||
}
|
}
|
||||||
if (n >= 0 && offset + n < buffer_.capacity()) {
|
if (n >= 0 && offset + n < buffer_.capacity()) {
|
||||||
if (sign) {
|
if (sign) {
|
||||||
if ((spec.align != ALIGN_RIGHT && spec.align != ALIGN_DEFAULT) ||
|
if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
|
||||||
*start != ' ') {
|
*start != ' ') {
|
||||||
*(start - 1) = sign;
|
*(start - 1) = sign;
|
||||||
sign = 0;
|
sign = 0;
|
||||||
} else {
|
} else {
|
||||||
*(start - 1) = spec.fill;
|
*(start - 1) = spec.fill();
|
||||||
}
|
}
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
if (spec.align == ALIGN_CENTER && spec.width > static_cast<unsigned>(n)) {
|
if (spec.align() == ALIGN_CENTER &&
|
||||||
char *p = GrowBuffer(spec.width);
|
spec.width() > static_cast<unsigned>(n)) {
|
||||||
std::copy(p, p + n, p + (spec.width - n) / 2);
|
char *p = GrowBuffer(spec.width());
|
||||||
FillPadding(p, spec.width, n, spec.fill);
|
std::copy(p, p + n, p + (spec.width() - n) / 2);
|
||||||
|
FillPadding(p, spec.width(), n, spec.fill());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (spec.fill != ' ' || sign) {
|
if (spec.fill() != ' ' || sign) {
|
||||||
while (*start == ' ')
|
while (*start == ' ')
|
||||||
*start++ = spec.fill;
|
*start++ = spec.fill();
|
||||||
if (sign)
|
if (sign)
|
||||||
*(start - 1) = sign;
|
*(start - 1) = sign;
|
||||||
}
|
}
|
||||||
@@ -404,15 +308,15 @@ void BasicFormatter::FormatDouble(
|
|||||||
char *BasicFormatter::FormatString(
|
char *BasicFormatter::FormatString(
|
||||||
const char *s, std::size_t size, const FormatSpec &spec) {
|
const char *s, std::size_t size, const FormatSpec &spec) {
|
||||||
char *out = 0;
|
char *out = 0;
|
||||||
if (spec.width > size) {
|
if (spec.width() > size) {
|
||||||
out = GrowBuffer(spec.width);
|
out = GrowBuffer(spec.width());
|
||||||
if (spec.align == ALIGN_RIGHT) {
|
if (spec.align() == ALIGN_RIGHT) {
|
||||||
std::fill_n(out, spec.width - size, spec.fill);
|
std::fill_n(out, spec.width() - size, spec.fill());
|
||||||
out += spec.width - size;
|
out += spec.width() - size;
|
||||||
} else if (spec.align == ALIGN_CENTER) {
|
} else if (spec.align() == ALIGN_CENTER) {
|
||||||
out = FillPadding(out, spec.width, size, spec.fill);
|
out = FillPadding(out, spec.width(), size, spec.fill());
|
||||||
} else {
|
} else {
|
||||||
std::fill_n(out + size, spec.width - size, spec.fill);
|
std::fill_n(out + size, spec.width() - size, spec.fill());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out = GrowBuffer(size);
|
out = GrowBuffer(size);
|
||||||
@@ -421,22 +325,6 @@ char *BasicFormatter::FormatString(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicFormatter::operator<<(int value) {
|
|
||||||
unsigned abs_value = value;
|
|
||||||
unsigned num_digits = 0;
|
|
||||||
char *out = 0;
|
|
||||||
if (value >= 0) {
|
|
||||||
num_digits = CountDigits(abs_value);
|
|
||||||
out = GrowBuffer(num_digits);
|
|
||||||
} else {
|
|
||||||
abs_value = 0 - abs_value;
|
|
||||||
num_digits = CountDigits(abs_value);
|
|
||||||
out = GrowBuffer(num_digits + 1);
|
|
||||||
*out++ = '-';
|
|
||||||
}
|
|
||||||
FormatDecimal(out, abs_value, num_digits);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -529,31 +417,31 @@ void Formatter::DoFormat() {
|
|||||||
// Parse fill and alignment.
|
// Parse fill and alignment.
|
||||||
if (char c = *s) {
|
if (char c = *s) {
|
||||||
const char *p = s + 1;
|
const char *p = s + 1;
|
||||||
spec.align = ALIGN_DEFAULT;
|
spec.align_ = ALIGN_DEFAULT;
|
||||||
do {
|
do {
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
case '<':
|
case '<':
|
||||||
spec.align = ALIGN_LEFT;
|
spec.align_ = ALIGN_LEFT;
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
spec.align = ALIGN_RIGHT;
|
spec.align_ = ALIGN_RIGHT;
|
||||||
break;
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
spec.align = ALIGN_NUMERIC;
|
spec.align_ = ALIGN_NUMERIC;
|
||||||
break;
|
break;
|
||||||
case '^':
|
case '^':
|
||||||
spec.align = ALIGN_CENTER;
|
spec.align_ = ALIGN_CENTER;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (spec.align != ALIGN_DEFAULT) {
|
if (spec.align_ != ALIGN_DEFAULT) {
|
||||||
if (p != s) {
|
if (p != s) {
|
||||||
if (c == '}') break;
|
if (c == '}') break;
|
||||||
if (c == '{')
|
if (c == '{')
|
||||||
ReportError(s, "invalid fill character '{'");
|
ReportError(s, "invalid fill character '{'");
|
||||||
s += 2;
|
s += 2;
|
||||||
spec.fill = c;
|
spec.fill_ = c;
|
||||||
} else ++s;
|
} else ++s;
|
||||||
if (spec.align == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE)
|
if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE)
|
||||||
ReportError(s, "format specifier '=' requires numeric argument");
|
ReportError(s, "format specifier '=' requires numeric argument");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -564,21 +452,21 @@ void Formatter::DoFormat() {
|
|||||||
switch (*s) {
|
switch (*s) {
|
||||||
case '+':
|
case '+':
|
||||||
CheckSign(s, arg);
|
CheckSign(s, arg);
|
||||||
spec.flags |= SIGN_FLAG | PLUS_FLAG;
|
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
|
||||||
break;
|
break;
|
||||||
case '-':
|
case '-':
|
||||||
CheckSign(s, arg);
|
CheckSign(s, arg);
|
||||||
break;
|
break;
|
||||||
case ' ':
|
case ' ':
|
||||||
CheckSign(s, arg);
|
CheckSign(s, arg);
|
||||||
spec.flags |= SIGN_FLAG;
|
spec.flags_ |= SIGN_FLAG;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*s == '#') {
|
if (*s == '#') {
|
||||||
if (arg.type > LAST_NUMERIC_TYPE)
|
if (arg.type > LAST_NUMERIC_TYPE)
|
||||||
ReportError(s, "format specifier '#' requires numeric argument");
|
ReportError(s, "format specifier '#' requires numeric argument");
|
||||||
spec.flags |= HASH_FLAG;
|
spec.flags_ |= HASH_FLAG;
|
||||||
++s;
|
++s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,15 +475,15 @@ void Formatter::DoFormat() {
|
|||||||
if (*s == '0') {
|
if (*s == '0') {
|
||||||
if (arg.type > LAST_NUMERIC_TYPE)
|
if (arg.type > LAST_NUMERIC_TYPE)
|
||||||
ReportError(s, "format specifier '0' requires numeric argument");
|
ReportError(s, "format specifier '0' requires numeric argument");
|
||||||
spec.align = ALIGN_NUMERIC;
|
spec.align_ = ALIGN_NUMERIC;
|
||||||
spec.fill = '0';
|
spec.fill_ = '0';
|
||||||
}
|
}
|
||||||
// Zero may be parsed again as a part of the width, but it is simpler
|
// Zero may be parsed again as a part of the width, but it is simpler
|
||||||
// and more efficient than checking if the next char is a digit.
|
// and more efficient than checking if the next char is a digit.
|
||||||
unsigned value = ParseUInt(s);
|
unsigned value = ParseUInt(s);
|
||||||
if (value > INT_MAX)
|
if (value > INT_MAX)
|
||||||
ReportError(s, "number is too big in format");
|
ReportError(s, "number is too big in format");
|
||||||
spec.width = value;
|
spec.width_ = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
@@ -649,7 +537,7 @@ void Formatter::DoFormat() {
|
|||||||
|
|
||||||
// Parse type.
|
// Parse type.
|
||||||
if (*s != '}' && *s)
|
if (*s != '}' && *s)
|
||||||
spec.type = *s++;
|
spec.type_ = *s++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*s++ != '}')
|
if (*s++ != '}')
|
||||||
@@ -677,18 +565,18 @@ void Formatter::DoFormat() {
|
|||||||
FormatDouble(arg.long_double_value, spec, precision);
|
FormatDouble(arg.long_double_value, spec, precision);
|
||||||
break;
|
break;
|
||||||
case CHAR: {
|
case CHAR: {
|
||||||
if (spec.type && spec.type != 'c')
|
if (spec.type_ && spec.type_ != 'c')
|
||||||
ReportUnknownType(spec.type, "char");
|
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_);
|
||||||
if (spec.align == ALIGN_RIGHT) {
|
if (spec.align_ == ALIGN_RIGHT) {
|
||||||
std::fill_n(out, spec.width - 1, spec.fill);
|
std::fill_n(out, spec.width_ - 1, spec.fill_);
|
||||||
out += spec.width - 1;
|
out += spec.width_ - 1;
|
||||||
} else if (spec.align == ALIGN_CENTER) {
|
} else if (spec.align_ == ALIGN_CENTER) {
|
||||||
out = FillPadding(out, spec.width, 1, spec.fill);
|
out = FillPadding(out, spec.width_, 1, spec.fill_);
|
||||||
} else {
|
} else {
|
||||||
std::fill_n(out + 1, spec.width - 1, spec.fill);
|
std::fill_n(out + 1, spec.width_ - 1, spec.fill_);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out = GrowBuffer(1);
|
out = GrowBuffer(1);
|
||||||
@@ -697,8 +585,8 @@ void Formatter::DoFormat() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STRING: {
|
case STRING: {
|
||||||
if (spec.type && spec.type != 's')
|
if (spec.type_ && spec.type_ != 's')
|
||||||
ReportUnknownType(spec.type, "string");
|
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) {
|
||||||
@@ -711,15 +599,15 @@ void Formatter::DoFormat() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case POINTER:
|
case POINTER:
|
||||||
if (spec.type && spec.type != 'p')
|
if (spec.type_ && spec.type_ != 'p')
|
||||||
ReportUnknownType(spec.type, "pointer");
|
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");
|
ReportUnknownType(spec.type_, "object");
|
||||||
(this->*arg.custom.format)(arg.custom.value, spec);
|
(this->*arg.custom.format)(arg.custom.value, spec);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
286
format.h
286
format.h
@@ -28,6 +28,8 @@
|
|||||||
#ifndef FORMAT_H_
|
#ifndef FORMAT_H_
|
||||||
#define FORMAT_H_
|
#define FORMAT_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -114,6 +116,25 @@ void Array<T, SIZE>::append(const T *begin, const T *end) {
|
|||||||
size_ += num_elements;
|
size_ += num_elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Information about an integer type.
|
||||||
|
template <typename T>
|
||||||
|
struct IntTraits {
|
||||||
|
typedef T UnsignedType;
|
||||||
|
static bool IsNegative(T) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct IntTraits<int> {
|
||||||
|
typedef unsigned UnsignedType;
|
||||||
|
static bool IsNegative(int value) { return value < 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct IntTraits<long> {
|
||||||
|
typedef unsigned long UnsignedType;
|
||||||
|
static bool IsNegative(long value) { return value < 0; }
|
||||||
|
};
|
||||||
|
|
||||||
class ArgInserter;
|
class ArgInserter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,19 +180,129 @@ enum Alignment {
|
|||||||
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FormatSpec {
|
// Flags.
|
||||||
Alignment align;
|
enum { SIGN_FLAG = 1, PLUS_FLAG = 2, HASH_FLAG = 4 };
|
||||||
unsigned flags;
|
|
||||||
unsigned width;
|
|
||||||
char type;
|
|
||||||
char fill;
|
|
||||||
|
|
||||||
FormatSpec(unsigned width = 0, char type = 0, char fill = ' ')
|
struct Spec {};
|
||||||
: align(ALIGN_DEFAULT), flags(0), width(width), type(type), fill(fill) {}
|
|
||||||
|
template <char TYPE>
|
||||||
|
struct TypeSpec : Spec {
|
||||||
|
Alignment align() const { return ALIGN_DEFAULT; }
|
||||||
|
unsigned width() const { return 0; }
|
||||||
|
|
||||||
|
bool sign_flag() const { return false; }
|
||||||
|
bool plus_flag() const { return false; }
|
||||||
|
bool hash_flag() const { return false; }
|
||||||
|
|
||||||
|
char type() const { return TYPE; }
|
||||||
|
char fill() const { return ' '; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct WidthSpec {
|
||||||
|
unsigned width_;
|
||||||
|
char fill_;
|
||||||
|
|
||||||
|
WidthSpec(unsigned width, char fill) : width_(width), fill_(fill) {}
|
||||||
|
|
||||||
|
unsigned width() const { return width_; }
|
||||||
|
char fill() const { return fill_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AlignSpec : WidthSpec {
|
||||||
|
Alignment align_;
|
||||||
|
|
||||||
|
AlignSpec(unsigned width, char fill)
|
||||||
|
: WidthSpec(width, fill), align_(ALIGN_DEFAULT) {}
|
||||||
|
|
||||||
|
Alignment align() const { return align_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <char TYPE>
|
||||||
|
struct AlignTypeSpec : AlignSpec {
|
||||||
|
AlignTypeSpec(unsigned width, char fill) : AlignSpec(width, fill) {}
|
||||||
|
|
||||||
|
bool sign_flag() const { return false; }
|
||||||
|
bool plus_flag() const { return false; }
|
||||||
|
bool hash_flag() const { return false; }
|
||||||
|
|
||||||
|
char type() const { return TYPE; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FormatSpec : AlignSpec {
|
||||||
|
unsigned flags_;
|
||||||
|
char type_;
|
||||||
|
|
||||||
|
FormatSpec(unsigned width = 0, char type = 0, char fill = ' ')
|
||||||
|
: AlignSpec(width, fill), flags_(0), type_(type) {}
|
||||||
|
|
||||||
|
Alignment align() const { return align_; }
|
||||||
|
|
||||||
|
bool sign_flag() const { return (flags_ & SIGN_FLAG) != 0; }
|
||||||
|
bool plus_flag() const { return (flags_ & PLUS_FLAG) != 0; }
|
||||||
|
bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; }
|
||||||
|
|
||||||
|
char type() const { return type_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Spec>
|
||||||
|
class IntFormatter : public Spec {
|
||||||
|
private:
|
||||||
|
T value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
IntFormatter(T value, const Spec &spec = Spec())
|
||||||
|
: Spec(spec), value_(value) {}
|
||||||
|
|
||||||
|
T value() const { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
inline IntFormatter<int, TypeSpec<'o'> > oct(int value) {
|
||||||
|
return IntFormatter<int, TypeSpec<'o'> >(value, TypeSpec<'o'>());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline IntFormatter<int, TypeSpec<'x'> > hex(int value) {
|
||||||
|
return IntFormatter<int, TypeSpec<'x'> >(value, TypeSpec<'x'>());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline IntFormatter<int, TypeSpec<'X'> > hexu(int value) {
|
||||||
|
return IntFormatter<int, TypeSpec<'X'> >(value, TypeSpec<'X'>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <char TYPE>
|
||||||
|
inline IntFormatter<int, AlignTypeSpec<TYPE> > pad(
|
||||||
|
IntFormatter<int, TypeSpec<TYPE> > f, unsigned width, char fill = ' ') {
|
||||||
|
return IntFormatter<int, AlignTypeSpec<TYPE> >(
|
||||||
|
f.value(), AlignTypeSpec<TYPE>(width, fill));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline IntFormatter<int, AlignTypeSpec<0> > pad(
|
||||||
|
int value, unsigned width, char fill = ' ') {
|
||||||
|
return IntFormatter<int, AlignTypeSpec<0> >(
|
||||||
|
value, AlignTypeSpec<0>(width, fill));
|
||||||
|
}
|
||||||
|
|
||||||
class BasicFormatter {
|
class BasicFormatter {
|
||||||
|
private:
|
||||||
|
static unsigned CountDigits(uint64_t n) {
|
||||||
|
unsigned count = 1;
|
||||||
|
for (;;) {
|
||||||
|
// Integer division is slow so do it for a group of four 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.
|
||||||
|
if (n < 10) return count;
|
||||||
|
if (n < 100) return count + 1;
|
||||||
|
if (n < 1000) return count + 2;
|
||||||
|
if (n < 10000) return count + 3;
|
||||||
|
n /= 10000u;
|
||||||
|
count += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FormatDecimal(char *buffer, uint64_t value, unsigned num_digits);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
static void ReportUnknownType(char code, const char *type);
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
@@ -183,11 +314,19 @@ class BasicFormatter {
|
|||||||
return &buffer_[size];
|
return &buffer_[size];
|
||||||
}
|
}
|
||||||
|
|
||||||
char *PrepareFilledBuffer(unsigned size, const FormatSpec &spec, char sign);
|
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);
|
||||||
|
|
||||||
// Formats an integer.
|
// Formats an integer.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void FormatInt(T value, const FormatSpec &spec);
|
void FormatInt(T value, const FormatSpec &spec) {
|
||||||
|
*this << IntFormatter<T, FormatSpec>(value, spec);
|
||||||
|
}
|
||||||
|
|
||||||
// Formats a floating point number (double or long double).
|
// Formats a floating point number (double or long double).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -231,23 +370,103 @@ class BasicFormatter {
|
|||||||
*/
|
*/
|
||||||
std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
|
std::string str() const { return std::string(&buffer_[0], buffer_.size()); }
|
||||||
|
|
||||||
void operator<<(int value);
|
BasicFormatter &operator<<(int value) {
|
||||||
|
return *this << IntFormatter<int, TypeSpec<0> >(value, TypeSpec<0>());
|
||||||
|
}
|
||||||
|
BasicFormatter &operator<<(unsigned value) {
|
||||||
|
return *this << IntFormatter<unsigned, TypeSpec<0> >(value, TypeSpec<0>());
|
||||||
|
}
|
||||||
|
|
||||||
void operator<<(char value) {
|
BasicFormatter &operator<<(char value) {
|
||||||
*GrowBuffer(1) = value;
|
*GrowBuffer(1) = value;
|
||||||
}
|
|
||||||
|
|
||||||
void operator<<(const char *value) {
|
|
||||||
std::size_t size = std::strlen(value);
|
|
||||||
std::strncpy(GrowBuffer(size), value, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
BasicFormatter &Write(int value, const FormatSpec &spec) {
|
|
||||||
FormatInt(value, spec);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BasicFormatter &operator<<(const char *value) {
|
||||||
|
std::size_t size = std::strlen(value);
|
||||||
|
std::strncpy(GrowBuffer(size), value, size);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename Spec>
|
||||||
|
BasicFormatter &operator<<(const IntFormatter<T, Spec> &f);
|
||||||
|
|
||||||
|
void Write(const std::string &s, const FormatSpec &spec) {
|
||||||
|
FormatString(s.data(), s.size(), spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear() {
|
||||||
|
buffer_.clear();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Spec>
|
||||||
|
BasicFormatter &BasicFormatter::operator<<(const IntFormatter<T, Spec> &f) {
|
||||||
|
T value = f.value();
|
||||||
|
unsigned size = 0;
|
||||||
|
char sign = 0;
|
||||||
|
typedef typename internal::IntTraits<T>::UnsignedType UnsignedType;
|
||||||
|
UnsignedType abs_value = value;
|
||||||
|
if (internal::IntTraits<T>::IsNegative(value)) {
|
||||||
|
sign = '-';
|
||||||
|
++size;
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
} else if (f.sign_flag()) {
|
||||||
|
sign = f.plus_flag() ? '+' : ' ';
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
BasicFormatter::FormatDecimal(p, abs_value, num_digits);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'x': case 'X': {
|
||||||
|
UnsignedType n = abs_value;
|
||||||
|
bool print_prefix = f.hash_flag();
|
||||||
|
if (print_prefix) size += 2;
|
||||||
|
do {
|
||||||
|
++size;
|
||||||
|
} while ((n >>= 4) != 0);
|
||||||
|
char *p = PrepareFilledBuffer(size, f, sign);
|
||||||
|
n = abs_value;
|
||||||
|
const char *digits = f.type() == 'x' ?
|
||||||
|
"0123456789abcdef" : "0123456789ABCDEF";
|
||||||
|
do {
|
||||||
|
*p-- = digits[n & 0xf];
|
||||||
|
} while ((n >>= 4) != 0);
|
||||||
|
if (print_prefix) {
|
||||||
|
*p-- = f.type();
|
||||||
|
*p = '0';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'o': {
|
||||||
|
UnsignedType n = abs_value;
|
||||||
|
bool print_prefix = f.hash_flag();
|
||||||
|
if (print_prefix) ++size;
|
||||||
|
do {
|
||||||
|
++size;
|
||||||
|
} while ((n >>= 3) != 0);
|
||||||
|
char *p = PrepareFilledBuffer(size, f, sign);
|
||||||
|
n = abs_value;
|
||||||
|
do {
|
||||||
|
*p-- = '0' + (n & 7);
|
||||||
|
} while ((n >>= 3) != 0);
|
||||||
|
if (print_prefix)
|
||||||
|
*p = '0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
BasicFormatter::ReportUnknownType(f.type(), "integer");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
The :cpp:class:`format::Formatter` class provides string formatting
|
The :cpp:class:`format::Formatter` class provides string formatting
|
||||||
@@ -382,7 +601,6 @@ class Formatter : public BasicFormatter {
|
|||||||
int next_arg_index_;
|
int next_arg_index_;
|
||||||
|
|
||||||
friend class internal::ArgInserter;
|
friend class internal::ArgInserter;
|
||||||
friend class ArgFormatter;
|
|
||||||
|
|
||||||
void Add(const Arg &arg) {
|
void Add(const Arg &arg) {
|
||||||
args_.push_back(&arg);
|
args_.push_back(&arg);
|
||||||
@@ -522,34 +740,18 @@ const char *c_str(ArgInserter::Proxy p);
|
|||||||
using format::internal::str;
|
using format::internal::str;
|
||||||
using format::internal::c_str;
|
using format::internal::c_str;
|
||||||
|
|
||||||
// ArgFormatter provides access to the format buffer within custom
|
|
||||||
// Format functions. It is not desirable to pass Formatter to these
|
|
||||||
// functions because Formatter::operator() is not reentrant and
|
|
||||||
// therefore can't be used for argument formatting.
|
|
||||||
class ArgFormatter {
|
|
||||||
private:
|
|
||||||
Formatter &formatter_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ArgFormatter(Formatter &f) : formatter_(f) {}
|
|
||||||
|
|
||||||
void Write(const std::string &s, const FormatSpec &spec) {
|
|
||||||
formatter_.FormatString(s.data(), s.size(), spec);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The default formatting function.
|
// The default formatting function.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Format(ArgFormatter &af, const FormatSpec &spec, const T &value) {
|
void Format(BasicFormatter &f, const FormatSpec &spec, const T &value) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << value;
|
os << value;
|
||||||
af.Write(os.str(), spec);
|
f.Write(os.str(), spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Formatter::FormatCustomArg(const void *arg, const FormatSpec &spec) {
|
void Formatter::FormatCustomArg(const void *arg, const FormatSpec &spec) {
|
||||||
ArgFormatter af(*this);
|
BasicFormatter &f = *this;
|
||||||
Format(af, spec, *static_cast<const T*>(arg));
|
Format(f, spec, *static_cast<const T*>(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline internal::ArgInserter Formatter::operator()(StringRef format) {
|
inline internal::ArgInserter Formatter::operator()(StringRef format) {
|
||||||
|
@@ -45,10 +45,15 @@ using std::size_t;
|
|||||||
using std::sprintf;
|
using std::sprintf;
|
||||||
|
|
||||||
using fmt::internal::Array;
|
using fmt::internal::Array;
|
||||||
|
using fmt::BasicFormatter;
|
||||||
using fmt::Formatter;
|
using fmt::Formatter;
|
||||||
using fmt::Format;
|
using fmt::Format;
|
||||||
using fmt::FormatError;
|
using fmt::FormatError;
|
||||||
using fmt::StringRef;
|
using fmt::StringRef;
|
||||||
|
using fmt::hex;
|
||||||
|
using fmt::hexu;
|
||||||
|
using fmt::oct;
|
||||||
|
using fmt::pad;
|
||||||
|
|
||||||
#define FORMAT_TEST_THROW_(statement, expected_exception, message, fail) \
|
#define FORMAT_TEST_THROW_(statement, expected_exception, message, fail) \
|
||||||
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
|
||||||
@@ -844,21 +849,6 @@ TEST(FormatterTest, FormatString) {
|
|||||||
EXPECT_EQ("test", str(Format("{0}") << std::string("test")));
|
EXPECT_EQ("test", str(Format("{0}") << std::string("test")));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ArgFormatterTest, Write) {
|
|
||||||
Formatter formatter;
|
|
||||||
fmt::ArgFormatter format(formatter);
|
|
||||||
fmt::FormatSpec spec;
|
|
||||||
spec.width = 2;
|
|
||||||
format.Write("12", spec);
|
|
||||||
EXPECT_EQ("12", formatter.str());
|
|
||||||
spec.width = 4;
|
|
||||||
format.Write("34", spec);
|
|
||||||
EXPECT_EQ("1234 ", formatter.str());
|
|
||||||
spec.width = 0;
|
|
||||||
format.Write("56", spec);
|
|
||||||
EXPECT_EQ("1234 56", formatter.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
class Date {
|
class Date {
|
||||||
int year_, month_, day_;
|
int year_, month_, day_;
|
||||||
public:
|
public:
|
||||||
@@ -880,8 +870,8 @@ TEST(FormatterTest, FormatUsingIOStreams) {
|
|||||||
|
|
||||||
class Answer {};
|
class Answer {};
|
||||||
|
|
||||||
void Format(fmt::ArgFormatter &af, const fmt::FormatSpec &spec, Answer) {
|
void Format(fmt::BasicFormatter &f, const fmt::FormatSpec &spec, Answer) {
|
||||||
af.Write("42", spec);
|
f.Write("42", spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, CustomFormat) {
|
TEST(FormatterTest, CustomFormat) {
|
||||||
@@ -1063,6 +1053,33 @@ TEST(TempFormatterTest, Examples) {
|
|||||||
ReportError("File not found: {0}") << path;
|
ReportError("File not found: {0}") << path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(StrTest, oct) {
|
||||||
|
BasicFormatter f;
|
||||||
|
f << oct(042);
|
||||||
|
EXPECT_EQ("42", f.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StrTest, hex) {
|
||||||
|
BasicFormatter f;
|
||||||
|
f << hex(0xbeef);
|
||||||
|
EXPECT_EQ("beef", f.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StrTest, hexu) {
|
||||||
|
BasicFormatter f;
|
||||||
|
f << hexu(0xbabe);
|
||||||
|
EXPECT_EQ("BABE", f.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(StrTest, pad) {
|
||||||
|
BasicFormatter f;
|
||||||
|
f << pad(hex(0xbeef), 8);
|
||||||
|
EXPECT_EQ(" beef", f.str());
|
||||||
|
f.Clear();
|
||||||
|
f << pad(42, 5, '0');
|
||||||
|
EXPECT_EQ("00042", f.str());
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::string str(const T &value) {
|
std::string str(const T &value) {
|
||||||
return fmt::str(fmt::Format("{0}") << value);
|
return fmt::str(fmt::Format("{0}") << value);
|
||||||
|
Reference in New Issue
Block a user