Commit 8474a623 authored by vitaut's avatar vitaut

Don't perform narrowing conversion for integers in printf (#255)

parent 22f61140
...@@ -278,8 +278,21 @@ class PrecisionHandler : ...@@ -278,8 +278,21 @@ class PrecisionHandler :
} }
}; };
// Converts an integer argument to an integral type T for printf. template <typename T, typename U>
struct is_same {
enum { value = 0 };
};
template <typename T> template <typename T>
struct is_same<T, T> {
enum { value = 1 };
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is not integral, the argument is converted
// to corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> { class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
private: private:
fmt::internal::Arg &arg_; fmt::internal::Arg &arg_;
...@@ -300,15 +313,17 @@ class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> { ...@@ -300,15 +313,17 @@ class ArgConverter : public fmt::internal::ArgVisitor<ArgConverter<T>, void> {
void visit_any_int(U value) { void visit_any_int(U value) {
bool is_signed = type_ == 'd' || type_ == 'i'; bool is_signed = type_ == 'd' || type_ == 'i';
using fmt::internal::Arg; using fmt::internal::Arg;
if (sizeof(T) <= sizeof(int)) { typedef typename fmt::internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
if (sizeof(TargetType) <= sizeof(int)) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { if (is_signed) {
arg_.type = Arg::INT; arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<T>(value)); arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
} else { } else {
arg_.type = Arg::UINT; arg_.type = Arg::UINT;
arg_.uint_value = static_cast<unsigned>( typedef typename fmt::internal::MakeUnsigned<TargetType>::Type Unsigned;
static_cast<typename fmt::internal::MakeUnsigned<T>::Type>(value)); arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
} }
} else { } else {
if (is_signed) { if (is_signed) {
...@@ -809,7 +824,7 @@ void fmt::internal::PrintfFormatter<Char>::format( ...@@ -809,7 +824,7 @@ void fmt::internal::PrintfFormatter<Char>::format(
break; break;
default: default:
--s; --s;
ArgConverter<int>(arg, *s).visit(arg); ArgConverter<void>(arg, *s).visit(arg);
} }
// Parse type. // Parse type.
......
...@@ -289,6 +289,7 @@ SPECIALIZE_MAKE_SIGNED(unsigned, int); ...@@ -289,6 +289,7 @@ SPECIALIZE_MAKE_SIGNED(unsigned, int);
SPECIALIZE_MAKE_SIGNED(unsigned long, long); SPECIALIZE_MAKE_SIGNED(unsigned long, long);
SPECIALIZE_MAKE_SIGNED(fmt::ULongLong, fmt::LongLong); SPECIALIZE_MAKE_SIGNED(fmt::ULongLong, fmt::LongLong);
// Test length format specifier ``length_spec``.
template <typename T, typename U> template <typename T, typename U>
void TestLength(const char *length_spec, U value) { void TestLength(const char *length_spec, U value) {
fmt::LongLong signed_value = value; fmt::LongLong signed_value = value;
...@@ -388,6 +389,13 @@ TEST(PrintfTest, Int) { ...@@ -388,6 +389,13 @@ TEST(PrintfTest, Int) {
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42); EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
} }
TEST(PrintfTest, LongLong) {
// fmt::printf allows passing long long arguments to %d without length
// specifiers.
fmt::LongLong max = std::numeric_limits<fmt::LongLong>::max();
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
}
TEST(PrintfTest, Float) { TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.650000", "%f", 392.65); EXPECT_PRINTF("392.650000", "%f", 392.65);
EXPECT_PRINTF("392.650000", "%F", 392.65); EXPECT_PRINTF("392.650000", "%F", 392.65);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment