Implement integer precision.
This commit is contained in:
parent
cb743c0249
commit
879838a539
72
format.cc
72
format.cc
@ -350,45 +350,6 @@ typename fmt::BasicWriter<Char>::CharPtr
|
|||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
typename fmt::BasicWriter<Char>::CharPtr
|
|
||||||
fmt::BasicWriter<Char>::PrepareFilledBuffer(unsigned num_digits,
|
|
||||||
const AlignSpec &spec, const char *prefix, unsigned prefix_size) {
|
|
||||||
unsigned size = prefix_size + num_digits;
|
|
||||||
unsigned width = spec.width();
|
|
||||||
if (width <= size) {
|
|
||||||
CharPtr p = GrowBuffer(size);
|
|
||||||
std::copy(prefix, prefix + prefix_size, p);
|
|
||||||
return p + size - 1;
|
|
||||||
}
|
|
||||||
CharPtr p = GrowBuffer(width);
|
|
||||||
CharPtr end = p + width;
|
|
||||||
Alignment align = spec.align();
|
|
||||||
// TODO: error if fill is not convertible to Char
|
|
||||||
Char fill = static_cast<Char>(spec.fill());
|
|
||||||
if (align == ALIGN_LEFT) {
|
|
||||||
std::copy(prefix, prefix + prefix_size, p);
|
|
||||||
p += size;
|
|
||||||
std::fill(p, end, fill);
|
|
||||||
} else if (align == ALIGN_CENTER) {
|
|
||||||
p = FillPadding(p, width, size, fill);
|
|
||||||
std::copy(prefix, prefix + prefix_size, p);
|
|
||||||
p += size;
|
|
||||||
} else {
|
|
||||||
if (align == ALIGN_NUMERIC) {
|
|
||||||
if (prefix_size != 0) {
|
|
||||||
p = std::copy(prefix, prefix + prefix_size, p);
|
|
||||||
size -= prefix_size;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
std::copy(prefix, prefix + prefix_size, end - size);
|
|
||||||
}
|
|
||||||
std::fill(p, end - size, fill);
|
|
||||||
p = end;
|
|
||||||
}
|
|
||||||
return p - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void fmt::BasicWriter<Char>::FormatDouble(
|
void fmt::BasicWriter<Char>::FormatDouble(
|
||||||
@ -637,7 +598,7 @@ unsigned fmt::BasicWriter<Char>::PrintfParser::ParseHeader(
|
|||||||
Char c = *s;
|
Char c = *s;
|
||||||
if (c >= '0' && c <= '9') {
|
if (c >= '0' && c <= '9') {
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
// preceded with a '0' flag.
|
// preceded with '0' flag(s).
|
||||||
unsigned value = internal::ParseNonnegativeInt(s, error);
|
unsigned value = internal::ParseNonnegativeInt(s, error);
|
||||||
if (*s == '$') { // value is an argument index
|
if (*s == '$') { // value is an argument index
|
||||||
++s;
|
++s;
|
||||||
@ -668,7 +629,7 @@ unsigned fmt::BasicWriter<Char>::PrintfParser::ParseHeader(
|
|||||||
return arg_index;
|
return arg_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this doesnt' depend on template argument
|
// TODO: move to a base class that doesn't depend on template argument
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
const typename fmt::BasicWriter<Char>::ArgInfo
|
const typename fmt::BasicWriter<Char>::ArgInfo
|
||||||
&fmt::BasicWriter<Char>::PrintfParser::HandleArgIndex(
|
&fmt::BasicWriter<Char>::PrintfParser::HandleArgIndex(
|
||||||
@ -738,16 +699,11 @@ void fmt::BasicWriter<Char>::PrintfParser::Format(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse precision.
|
// Parse precision.
|
||||||
int precision = -1;
|
if (*s == '.') {
|
||||||
/*if (*s == '.') {
|
|
||||||
++s;
|
++s;
|
||||||
precision = 0;
|
if ('0' <= *s && *s <= '9')
|
||||||
if ('0' <= *s && *s <= '9') {
|
spec.precision_ = internal::ParseNonnegativeInt(s, error);
|
||||||
unsigned value = ParseNonnegativeInt(s);
|
/*else if (*s == '*') {
|
||||||
if (value > INT_MAX)
|
|
||||||
ReportError(s, "number is too big in format");
|
|
||||||
precision = value;
|
|
||||||
} else if (*s == '{') {
|
|
||||||
++s;
|
++s;
|
||||||
++num_open_braces_;
|
++num_open_braces_;
|
||||||
const ArgInfo &precision_arg = ParseArgIndex(s);
|
const ArgInfo &precision_arg = ParseArgIndex(s);
|
||||||
@ -792,8 +748,8 @@ void fmt::BasicWriter<Char>::PrintfParser::Format(
|
|||||||
if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
|
if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
|
||||||
ReportError(s,
|
ReportError(s,
|
||||||
"precision specifier requires floating-point argument");
|
"precision specifier requires floating-point argument");
|
||||||
}
|
}*/
|
||||||
}*/
|
}
|
||||||
|
|
||||||
// Parse type.
|
// Parse type.
|
||||||
if (!*s)
|
if (!*s)
|
||||||
@ -825,10 +781,10 @@ void fmt::BasicWriter<Char>::PrintfParser::Format(
|
|||||||
writer.FormatInt(arg.ulong_long_value, spec);
|
writer.FormatInt(arg.ulong_long_value, spec);
|
||||||
break;
|
break;
|
||||||
case DOUBLE:
|
case DOUBLE:
|
||||||
writer.FormatDouble(arg.double_value, spec, precision);
|
writer.FormatDouble(arg.double_value, spec, spec.precision_);
|
||||||
break;
|
break;
|
||||||
case LONG_DOUBLE:
|
case LONG_DOUBLE:
|
||||||
writer.FormatDouble(arg.long_double_value, spec, precision);
|
writer.FormatDouble(arg.long_double_value, spec, spec.precision_);
|
||||||
break;
|
break;
|
||||||
case CHAR: {
|
case CHAR: {
|
||||||
if (spec.type_ && spec.type_ != 'c')
|
if (spec.type_ && spec.type_ != 'c')
|
||||||
@ -1175,10 +1131,6 @@ template fmt::BasicWriter<char>::CharPtr
|
|||||||
fmt::BasicWriter<char>::FillPadding(CharPtr buffer,
|
fmt::BasicWriter<char>::FillPadding(CharPtr buffer,
|
||||||
unsigned total_size, std::size_t content_size, wchar_t fill);
|
unsigned total_size, std::size_t content_size, wchar_t fill);
|
||||||
|
|
||||||
template fmt::BasicWriter<char>::CharPtr
|
|
||||||
fmt::BasicWriter<char>::PrepareFilledBuffer(unsigned num_digits,
|
|
||||||
const AlignSpec &spec, const char *prefix, unsigned prefix_size);
|
|
||||||
|
|
||||||
template void fmt::BasicWriter<char>::FormatParser::Format(
|
template void fmt::BasicWriter<char>::FormatParser::Format(
|
||||||
BasicWriter<char> &writer, BasicStringRef<char> format,
|
BasicWriter<char> &writer, BasicStringRef<char> format,
|
||||||
std::size_t num_args, const ArgInfo *args);
|
std::size_t num_args, const ArgInfo *args);
|
||||||
@ -1193,10 +1145,6 @@ template fmt::BasicWriter<wchar_t>::CharPtr
|
|||||||
fmt::BasicWriter<wchar_t>::FillPadding(CharPtr buffer,
|
fmt::BasicWriter<wchar_t>::FillPadding(CharPtr buffer,
|
||||||
unsigned total_size, std::size_t content_size, wchar_t fill);
|
unsigned total_size, std::size_t content_size, wchar_t fill);
|
||||||
|
|
||||||
template fmt::BasicWriter<wchar_t>::CharPtr
|
|
||||||
fmt::BasicWriter<wchar_t>::PrepareFilledBuffer(unsigned num_digits,
|
|
||||||
const AlignSpec &spec, const char *prefix, unsigned prefix_size);
|
|
||||||
|
|
||||||
template void fmt::BasicWriter<wchar_t>::FormatParser::Format(
|
template void fmt::BasicWriter<wchar_t>::FormatParser::Format(
|
||||||
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
|
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
|
||||||
std::size_t num_args, const ArgInfo *args);
|
std::size_t num_args, const ArgInfo *args);
|
||||||
|
61
format.h
61
format.h
@ -586,6 +586,7 @@ template <char TYPE>
|
|||||||
struct TypeSpec : EmptySpec {
|
struct TypeSpec : EmptySpec {
|
||||||
Alignment align() const { return ALIGN_DEFAULT; }
|
Alignment align() const { return ALIGN_DEFAULT; }
|
||||||
unsigned width() const { return 0; }
|
unsigned width() const { return 0; }
|
||||||
|
int precision() const { return -1; }
|
||||||
|
|
||||||
bool sign_flag() const { return false; }
|
bool sign_flag() const { return false; }
|
||||||
bool plus_flag() const { return false; }
|
bool plus_flag() const { return false; }
|
||||||
@ -616,6 +617,8 @@ struct AlignSpec : WidthSpec {
|
|||||||
: WidthSpec(width, fill), align_(align) {}
|
: WidthSpec(width, fill), align_(align) {}
|
||||||
|
|
||||||
Alignment align() const { return align_; }
|
Alignment align() const { return align_; }
|
||||||
|
|
||||||
|
int precision() const { return -1; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// An alignment and type specifier.
|
// An alignment and type specifier.
|
||||||
@ -633,15 +636,19 @@ struct AlignTypeSpec : AlignSpec {
|
|||||||
// A full format specifier.
|
// A full format specifier.
|
||||||
struct FormatSpec : AlignSpec {
|
struct FormatSpec : AlignSpec {
|
||||||
unsigned flags_;
|
unsigned flags_;
|
||||||
|
int precision_;
|
||||||
char type_;
|
char type_;
|
||||||
|
|
||||||
FormatSpec(unsigned width = 0, char type = 0, wchar_t fill = ' ')
|
FormatSpec(
|
||||||
: AlignSpec(width, fill), flags_(0), type_(type) {}
|
unsigned width = 0, char type = 0, wchar_t fill = ' ')
|
||||||
|
: AlignSpec(width, fill), flags_(0), precision_(-1), type_(type) {}
|
||||||
|
|
||||||
bool sign_flag() const { return (flags_ & SIGN_FLAG) != 0; }
|
bool sign_flag() const { return (flags_ & SIGN_FLAG) != 0; }
|
||||||
bool plus_flag() const { return (flags_ & PLUS_FLAG) != 0; }
|
bool plus_flag() const { return (flags_ & PLUS_FLAG) != 0; }
|
||||||
bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; }
|
bool hash_flag() const { return (flags_ & HASH_FLAG) != 0; }
|
||||||
|
|
||||||
|
int precision() const { return precision_; }
|
||||||
|
|
||||||
char type() const { return type_; }
|
char type() const { return type_; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -860,14 +867,16 @@ class BasicWriter {
|
|||||||
return p + size - 1;
|
return p + size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Spec>
|
||||||
CharPtr PrepareFilledBuffer(unsigned num_digits,
|
CharPtr PrepareFilledBuffer(unsigned num_digits,
|
||||||
const AlignSpec &spec, const char *prefix, unsigned prefix_size);
|
const Spec &spec, const char *prefix, unsigned prefix_size);
|
||||||
|
|
||||||
// Formats an integer.
|
// Formats an integer.
|
||||||
template <typename T, typename Spec>
|
template <typename T, typename Spec>
|
||||||
void FormatInt(T value, const Spec &spec);
|
void FormatInt(T value, const Spec &spec);
|
||||||
|
|
||||||
// Formats a floating-point number (double or long double).
|
// Formats a floating-point number (double or long double).
|
||||||
|
// TODO: use precision from spec
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void FormatDouble(T value, const FormatSpec &spec, int precision);
|
void FormatDouble(T value, const FormatSpec &spec, int precision);
|
||||||
|
|
||||||
@ -1294,6 +1303,52 @@ typename BasicWriter<Char>::CharPtr BasicWriter<Char>::FormatString(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
template <typename Spec>
|
||||||
|
typename fmt::BasicWriter<Char>::CharPtr
|
||||||
|
fmt::BasicWriter<Char>::PrepareFilledBuffer(unsigned num_digits,
|
||||||
|
const Spec &spec, const char *prefix, unsigned prefix_size) {
|
||||||
|
if (spec.precision() >= 0) {
|
||||||
|
// TODO: fill up to width if necessary
|
||||||
|
return PrepareFilledBuffer(num_digits,
|
||||||
|
AlignSpec(spec.precision() + prefix_size, '0', ALIGN_NUMERIC),
|
||||||
|
prefix, prefix_size);
|
||||||
|
}
|
||||||
|
unsigned size = prefix_size + num_digits;
|
||||||
|
unsigned width = spec.width();
|
||||||
|
if (width <= size) {
|
||||||
|
CharPtr p = GrowBuffer(size);
|
||||||
|
std::copy(prefix, prefix + prefix_size, p);
|
||||||
|
return p + size - 1;
|
||||||
|
}
|
||||||
|
CharPtr p = GrowBuffer(width);
|
||||||
|
CharPtr end = p + width;
|
||||||
|
Alignment align = spec.align();
|
||||||
|
// TODO: error if fill is not convertible to Char
|
||||||
|
Char fill = static_cast<Char>(spec.fill());
|
||||||
|
if (align == ALIGN_LEFT) {
|
||||||
|
std::copy(prefix, prefix + prefix_size, p);
|
||||||
|
p += size;
|
||||||
|
std::fill(p, end, fill);
|
||||||
|
} else if (align == ALIGN_CENTER) {
|
||||||
|
p = FillPadding(p, width, size, fill);
|
||||||
|
std::copy(prefix, prefix + prefix_size, p);
|
||||||
|
p += size;
|
||||||
|
} else {
|
||||||
|
if (align == ALIGN_NUMERIC) {
|
||||||
|
if (prefix_size != 0) {
|
||||||
|
p = std::copy(prefix, prefix + prefix_size, p);
|
||||||
|
size -= prefix_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::copy(prefix, prefix + prefix_size, end - size);
|
||||||
|
}
|
||||||
|
std::fill(p, end - size, fill);
|
||||||
|
p = end;
|
||||||
|
}
|
||||||
|
return p - 1;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
template <typename T, typename Spec>
|
template <typename T, typename Spec>
|
||||||
void BasicWriter<Char>::FormatInt(T value, const Spec &spec) {
|
void BasicWriter<Char>::FormatInt(T value, const Spec &spec) {
|
||||||
|
@ -133,27 +133,6 @@ TEST(PrintfTest, DefaultAlignRight) {
|
|||||||
EXPECT_PRINTF(" abc", "%5s", "abc");
|
EXPECT_PRINTF(" abc", "%5s", "abc");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Width) {
|
|
||||||
EXPECT_PRINTF(" abc", "%5s", "abc");
|
|
||||||
|
|
||||||
// Width cannot be specified twice.
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
|
|
||||||
"unknown format code '-' for integer");
|
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%{}d", BIG_NUM)), 42),
|
|
||||||
FormatError, "number is too big in format");
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%1${}d", BIG_NUM)), 42),
|
|
||||||
FormatError, "number is too big in format");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PrintfTest, DynamicWidth) {
|
|
||||||
EXPECT_EQ(" 42", str(fmt::sprintf("%*d", 5, 42)));
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), FormatError,
|
|
||||||
"width is not integer");
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d"), FormatError,
|
|
||||||
"argument index is out of range in format");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(PrintfTest, ZeroFlag) {
|
TEST(PrintfTest, ZeroFlag) {
|
||||||
EXPECT_PRINTF("00042", "%05d", 42);
|
EXPECT_PRINTF("00042", "%05d", 42);
|
||||||
EXPECT_PRINTF("-0042", "%05d", -42);
|
EXPECT_PRINTF("-0042", "%05d", -42);
|
||||||
@ -227,6 +206,31 @@ TEST(PrintfTest, HashFlag) {
|
|||||||
EXPECT_PRINTF("x", "%#c", 'x');
|
EXPECT_PRINTF("x", "%#c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test '*' width, precision, length and type specifier
|
TEST(PrintfTest, Width) {
|
||||||
|
EXPECT_PRINTF(" abc", "%5s", "abc");
|
||||||
|
|
||||||
|
// Width cannot be specified twice.
|
||||||
|
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
|
||||||
|
"unknown format code '-' for integer");
|
||||||
|
|
||||||
|
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%{}d", BIG_NUM)), 42),
|
||||||
|
FormatError, "number is too big in format");
|
||||||
|
EXPECT_THROW_MSG(fmt::sprintf(str(Format("%1${}d", BIG_NUM)), 42),
|
||||||
|
FormatError, "number is too big in format");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PrintfTest, DynamicWidth) {
|
||||||
|
EXPECT_EQ(" 42", str(fmt::sprintf("%*d", 5, 42)));
|
||||||
|
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), FormatError,
|
||||||
|
"width is not integer");
|
||||||
|
EXPECT_THROW_MSG(fmt::sprintf("%*d"), FormatError,
|
||||||
|
"argument index is out of range in format");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(PrintfTest, Precision) {
|
||||||
|
EXPECT_PRINTF("00042", "%.5d", 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test precision, length and type specifier
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user