forked from fmtlib/fmt
Improve handling of dynamic precision in printf.
This commit is contained in:
45
format.cc
45
format.cc
@@ -125,20 +125,12 @@ void report_error(FormatFunc func,
|
|||||||
|
|
||||||
const Arg DUMMY_ARG = {Arg::INT, 0};
|
const Arg DUMMY_ARG = {Arg::INT, 0};
|
||||||
|
|
||||||
fmt::ULongLong get_int_value(const Arg &arg) {
|
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
|
||||||
switch (arg.type) {
|
class IsZeroInt : public fmt::internal::ArgVisitor<IsZeroInt, bool> {
|
||||||
case Arg::INT:
|
public:
|
||||||
return arg.int_value;
|
template <typename T>
|
||||||
case Arg::UINT:
|
bool visit_any_int(T value) { return value == 0; }
|
||||||
return arg.uint_value;
|
};
|
||||||
case Arg::LONG_LONG:
|
|
||||||
return arg.long_long_value;
|
|
||||||
case Arg::ULONG_LONG:
|
|
||||||
return arg.ulong_long_value;
|
|
||||||
default:
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parses an unsigned integer advancing s to the end of the parsed input.
|
// Parses an unsigned integer advancing s to the end of the parsed input.
|
||||||
// This function assumes that the first character of s is a digit.
|
// This function assumes that the first character of s is a digit.
|
||||||
@@ -175,7 +167,7 @@ const Char *find_closing_brace(const Char *s, int num_open_braces = 1) {
|
|||||||
|
|
||||||
// Checks if an argument is a valid printf width specifier and sets
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
// left alignment if it is negative.
|
// left alignment if it is negative.
|
||||||
struct WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
|
class WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
|
||||||
private:
|
private:
|
||||||
fmt::FormatSpec &spec_;
|
fmt::FormatSpec &spec_;
|
||||||
|
|
||||||
@@ -200,6 +192,21 @@ struct WidthHandler : public fmt::internal::ArgVisitor<WidthHandler, unsigned> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PrecisionHandler :
|
||||||
|
public fmt::internal::ArgVisitor<PrecisionHandler, int> {
|
||||||
|
public:
|
||||||
|
unsigned visit_unhandled_arg() {
|
||||||
|
throw fmt::FormatError("precision is not integer");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int visit_any_int(T value) {
|
||||||
|
if (value < INT_MIN || value > INT_MAX)
|
||||||
|
throw fmt::FormatError("number is too big in format");
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// This function template is used to prevent compile errors when handling
|
// This function template is used to prevent compile errors when handling
|
||||||
// incompatible string arguments, e.g. handling a wide string in a narrow
|
// incompatible string arguments, e.g. handling a wide string in a narrow
|
||||||
// string formatter.
|
// string formatter.
|
||||||
@@ -848,16 +855,12 @@ void fmt::internal::PrintfFormatter<Char>::format(
|
|||||||
spec.precision_ = parse_nonnegative_int(s, error_);
|
spec.precision_ = parse_nonnegative_int(s, error_);
|
||||||
} else if (*s == '*') {
|
} else if (*s == '*') {
|
||||||
++s;
|
++s;
|
||||||
const Arg &arg = handle_arg_index(UINT_MAX);
|
spec.precision_ = PrecisionHandler().visit(handle_arg_index(UINT_MAX));
|
||||||
if (arg.type <= Arg::LAST_INTEGER_TYPE)
|
|
||||||
spec.precision_ = static_cast<int>(get_int_value(arg)); // TODO: check for overflow
|
|
||||||
else if (!error_)
|
|
||||||
error_ = "precision is not integer";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Arg &arg = handle_arg_index(arg_index);
|
const Arg &arg = handle_arg_index(arg_index);
|
||||||
if (spec.flag(HASH_FLAG) && get_int_value(arg) == 0)
|
if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg))
|
||||||
spec.flags_ &= ~HASH_FLAG;
|
spec.flags_ &= ~HASH_FLAG;
|
||||||
if (spec.fill_ == '0') {
|
if (spec.fill_ == '0') {
|
||||||
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
|
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
|
||||||
|
@@ -258,14 +258,18 @@ TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
|
|||||||
|
|
||||||
TEST(PrintfTest, DynamicPrecision) {
|
TEST(PrintfTest, DynamicPrecision) {
|
||||||
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
|
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
|
||||||
// TODO
|
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
|
||||||
//EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), FormatError,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), FormatError,
|
||||||
"argument index is out of range in format");
|
"argument index is out of range in format");
|
||||||
//EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), FormatError,
|
||||||
// "number is too big in format");
|
"number is too big in format");
|
||||||
|
if (sizeof(fmt::LongLong) != sizeof(int)) {
|
||||||
|
fmt::LongLong prec = static_cast<fmt::LongLong>(INT_MIN) - 1;
|
||||||
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), FormatError,
|
||||||
|
"number is too big in format");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Length) {
|
TEST(PrintfTest, Length) {
|
||||||
|
Reference in New Issue
Block a user