mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-26 12:10:06 +00:00
Implement left alignment.
This commit is contained in:
parent
c5f4ecb25a
commit
be6e54de36
73
format.cc
73
format.cc
@ -53,12 +53,7 @@ using fmt::FormatSpec;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Flags.
|
// Flags.
|
||||||
enum { PLUS_FLAG = 1, ZERO_FLAG = 2, HEX_PREFIX_FLAG = 4 };
|
enum { PLUS_FLAG = 1, HEX_PREFIX_FLAG = 2 };
|
||||||
|
|
||||||
// Alignment.
|
|
||||||
enum Alignment {
|
|
||||||
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
|
||||||
};
|
|
||||||
|
|
||||||
void ReportUnknownType(char code, const char *type) {
|
void ReportUnknownType(char code, const char *type) {
|
||||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
if (std::isprint(static_cast<unsigned char>(code))) {
|
||||||
@ -111,6 +106,34 @@ void Formatter::ReportError(const char *s, const std::string &message) const {
|
|||||||
throw fmt::FormatError("unmatched '{' in format");
|
throw fmt::FormatError("unmatched '{' in format");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *Formatter::PrepareFilledBuffer(
|
||||||
|
unsigned size, FormatSpec spec, char sign) {
|
||||||
|
if (spec.width <= size) {
|
||||||
|
char *p = GrowBuffer(size);
|
||||||
|
*p = sign;
|
||||||
|
return p + size - 1;
|
||||||
|
}
|
||||||
|
char *p = GrowBuffer(spec.width);
|
||||||
|
char *end = p + spec.width;
|
||||||
|
if (spec.align == ALIGN_LEFT) {
|
||||||
|
*p = sign;
|
||||||
|
p += size;
|
||||||
|
std::fill(p, end, spec.fill);
|
||||||
|
} else if (spec.align == ALIGN_NUMERIC) {
|
||||||
|
if (sign) {
|
||||||
|
*p++ = sign;
|
||||||
|
--size;
|
||||||
|
}
|
||||||
|
std::fill(p, end - size, spec.fill);
|
||||||
|
p = end;
|
||||||
|
} else {
|
||||||
|
*(end - size) = sign;
|
||||||
|
std::fill(p, end - size, spec.fill);
|
||||||
|
p = end;
|
||||||
|
}
|
||||||
|
return p - 1;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Formatter::FormatInt(T value, FormatSpec spec) {
|
void Formatter::FormatInt(T value, FormatSpec spec) {
|
||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
@ -125,16 +148,13 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
|
|||||||
sign = '+';
|
sign = '+';
|
||||||
++size;
|
++size;
|
||||||
}
|
}
|
||||||
size_t start = buffer_.size();
|
|
||||||
char *p = 0;
|
|
||||||
switch (spec.type) {
|
switch (spec.type) {
|
||||||
case 0: case 'd': {
|
case 0: case 'd': {
|
||||||
UnsignedType n = abs_value;
|
UnsignedType n = abs_value;
|
||||||
do {
|
do {
|
||||||
++size;
|
++size;
|
||||||
} while ((n /= 10) != 0);
|
} while ((n /= 10) != 0);
|
||||||
unsigned width = std::max(spec.width, size);
|
char *p = PrepareFilledBuffer(size, spec, sign);
|
||||||
p = GrowBuffer(width) + width - 1;
|
|
||||||
n = abs_value;
|
n = abs_value;
|
||||||
do {
|
do {
|
||||||
*p-- = '0' + (n % 10);
|
*p-- = '0' + (n % 10);
|
||||||
@ -148,8 +168,7 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
|
|||||||
do {
|
do {
|
||||||
++size;
|
++size;
|
||||||
} while ((n >>= 4) != 0);
|
} while ((n >>= 4) != 0);
|
||||||
unsigned width = std::max(spec.width, size);
|
char *p = PrepareFilledBuffer(size, spec, sign);
|
||||||
p = GrowBuffer(width) + width - 1;
|
|
||||||
n = abs_value;
|
n = abs_value;
|
||||||
const char *digits = spec.type == 'x' ?
|
const char *digits = spec.type == 'x' ?
|
||||||
"0123456789abcdef" : "0123456789ABCDEF";
|
"0123456789abcdef" : "0123456789ABCDEF";
|
||||||
@ -167,8 +186,7 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
|
|||||||
do {
|
do {
|
||||||
++size;
|
++size;
|
||||||
} while ((n >>= 3) != 0);
|
} while ((n >>= 3) != 0);
|
||||||
unsigned width = std::max(spec.width, size);
|
char *p = PrepareFilledBuffer(size, spec, sign);
|
||||||
p = GrowBuffer(width) + width - 1;
|
|
||||||
n = abs_value;
|
n = abs_value;
|
||||||
do {
|
do {
|
||||||
*p-- = '0' + (n & 7);
|
*p-- = '0' + (n & 7);
|
||||||
@ -179,13 +197,6 @@ void Formatter::FormatInt(T value, FormatSpec spec) {
|
|||||||
ReportUnknownType(spec.type, "integer");
|
ReportUnknownType(spec.type, "integer");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (sign) {
|
|
||||||
if ((spec.flags & ZERO_FLAG) != 0)
|
|
||||||
buffer_[start++] = sign;
|
|
||||||
else
|
|
||||||
*p-- = sign;
|
|
||||||
}
|
|
||||||
std::fill(&buffer_[start], p + 1, spec.fill);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -210,14 +221,16 @@ void Formatter::FormatDouble(T value, const FormatSpec &spec, int precision) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build format string.
|
// Build format string.
|
||||||
enum { MAX_FORMAT_SIZE = 9}; // longest format: %+0*.*Lg
|
enum { MAX_FORMAT_SIZE = 10}; // longest format: %+0*.*Lg
|
||||||
char format[MAX_FORMAT_SIZE];
|
char format[MAX_FORMAT_SIZE];
|
||||||
char *format_ptr = format;
|
char *format_ptr = format;
|
||||||
unsigned width = spec.width;
|
unsigned width = spec.width;
|
||||||
*format_ptr++ = '%';
|
*format_ptr++ = '%';
|
||||||
if ((spec.flags & PLUS_FLAG) != 0)
|
if ((spec.flags & PLUS_FLAG) != 0)
|
||||||
*format_ptr++ = '+';
|
*format_ptr++ = '+';
|
||||||
if ((spec.flags & ZERO_FLAG) != 0)
|
if (spec.align == ALIGN_LEFT)
|
||||||
|
*format_ptr++ = '-';
|
||||||
|
else if (spec.align == ALIGN_NUMERIC)
|
||||||
*format_ptr++ = '0';
|
*format_ptr++ = '0';
|
||||||
if (width != 0)
|
if (width != 0)
|
||||||
*format_ptr++ = '*';
|
*format_ptr++ = '*';
|
||||||
@ -307,23 +320,23 @@ 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;
|
||||||
Alignment align = ALIGN_DEFAULT;
|
spec.align = ALIGN_DEFAULT;
|
||||||
do {
|
do {
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
case '<':
|
case '<':
|
||||||
align = ALIGN_LEFT;
|
spec.align = ALIGN_LEFT;
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
align = ALIGN_RIGHT;
|
spec.align = ALIGN_RIGHT;
|
||||||
break;
|
break;
|
||||||
case '=':
|
case '=':
|
||||||
align = ALIGN_NUMERIC;
|
spec.align = ALIGN_NUMERIC;
|
||||||
break;
|
break;
|
||||||
case '^':
|
case '^':
|
||||||
align = ALIGN_CENTER;
|
spec.align = ALIGN_CENTER;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (align != ALIGN_DEFAULT) {
|
if (spec.align != ALIGN_DEFAULT) {
|
||||||
if (p != s && c != '{' && c != '}') {
|
if (p != s && c != '{' && c != '}') {
|
||||||
s += 2;
|
s += 2;
|
||||||
spec.fill = c;
|
spec.fill = c;
|
||||||
@ -350,7 +363,7 @@ 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.flags |= ZERO_FLAG;
|
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
|
||||||
|
9
format.h
9
format.h
@ -123,13 +123,18 @@ class FormatError : public std::runtime_error {
|
|||||||
: std::runtime_error(message) {}
|
: std::runtime_error(message) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Alignment {
|
||||||
|
ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC
|
||||||
|
};
|
||||||
|
|
||||||
struct FormatSpec {
|
struct FormatSpec {
|
||||||
|
Alignment align;
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
unsigned width;
|
unsigned width;
|
||||||
char type;
|
char type;
|
||||||
char fill;
|
char fill;
|
||||||
|
|
||||||
FormatSpec() : flags(0), width(0), type(0), fill(' ') {}
|
FormatSpec() : align(ALIGN_DEFAULT), flags(0), width(0), type(0), fill(' ') {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Formatter provides string formatting functionality similar to Python's
|
// Formatter provides string formatting functionality similar to Python's
|
||||||
@ -266,6 +271,8 @@ class Formatter {
|
|||||||
|
|
||||||
void ReportError(const char *s, const std::string &message) const;
|
void ReportError(const char *s, const std::string &message) const;
|
||||||
|
|
||||||
|
char *PrepareFilledBuffer(unsigned size, FormatSpec spec, char sign);
|
||||||
|
|
||||||
// Formats an integer.
|
// Formats an integer.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void FormatInt(T value, FormatSpec spec);
|
void FormatInt(T value, FormatSpec spec);
|
||||||
|
@ -259,19 +259,20 @@ TEST(FormatterTest, EmptySpecs) {
|
|||||||
EXPECT_EQ("42", str(Format("{0:}") << 42));
|
EXPECT_EQ("42", str(Format("{0:}") << 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, Align) {
|
TEST(FormatterTest, LeftAlign) {
|
||||||
// TODO
|
EXPECT_EQ("42 ", str(Format("{0:<4}") << 42));
|
||||||
EXPECT_EQ(" 42", str(Format("{0:>4}") << 42));
|
EXPECT_EQ("42 ", str(Format("{0:<4o}") << 042));
|
||||||
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42));
|
EXPECT_EQ("42 ", str(Format("{0:<4x}") << 0x42));
|
||||||
EXPECT_EQ(" 42", str(Format("{0:>5}") << 42u));
|
EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42));
|
||||||
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42l));
|
EXPECT_EQ("42 ", str(Format("{0:<5}") << 42u));
|
||||||
EXPECT_EQ(" 42", str(Format("{0:>5}") << 42ul));
|
EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42l));
|
||||||
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42.0));
|
EXPECT_EQ("42 ", str(Format("{0:<5}") << 42ul));
|
||||||
EXPECT_EQ(" -42", str(Format("{0:>5}") << -42.0l));
|
EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42.0));
|
||||||
|
EXPECT_EQ("-42 ", str(Format("{0:<5}") << -42.0l));
|
||||||
EXPECT_EQ("c ", str(Format("{0:<5}") << 'c'));
|
EXPECT_EQ("c ", str(Format("{0:<5}") << 'c'));
|
||||||
EXPECT_EQ("abc ", str(Format("{0:<5}") << "abc"));
|
EXPECT_EQ("abc ", str(Format("{0:<5}") << "abc"));
|
||||||
EXPECT_EQ(" 0xface",
|
EXPECT_EQ("0xface ",
|
||||||
str(Format("{0:>8}") << reinterpret_cast<void*>(0xface)));
|
str(Format("{0:<8}") << reinterpret_cast<void*>(0xface)));
|
||||||
EXPECT_EQ("def ", str(Format("{0:<5}") << TestString("def")));
|
EXPECT_EQ("def ", str(Format("{0:<5}") << TestString("def")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user