From 8474a6232d3b23fcf240329b579dd6f8284de54f Mon Sep 17 00:00:00 2001 From: vitaut Date: Sun, 24 Jan 2016 00:43:42 +0100 Subject: [PATCH] Don't perform narrowing conversion for integers in printf (#255) --- format.cc | 27 +++++++++++++++++++++------ test/printf-test.cc | 8 ++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/format.cc b/format.cc index 8ad96c7e..b85d7a76 100644 --- a/format.cc +++ b/format.cc @@ -278,8 +278,21 @@ class PrecisionHandler : } }; -// Converts an integer argument to an integral type T for printf. +template +struct is_same { + enum { value = 0 }; +}; + template +struct is_same { + 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 class ArgConverter : public fmt::internal::ArgVisitor, void> { private: fmt::internal::Arg &arg_; @@ -300,15 +313,17 @@ class ArgConverter : public fmt::internal::ArgVisitor, void> { void visit_any_int(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; using fmt::internal::Arg; - if (sizeof(T) <= sizeof(int)) { + typedef typename fmt::internal::Conditional< + is_same::value, U, T>::type TargetType; + if (sizeof(TargetType) <= sizeof(int)) { // Extra casts are used to silence warnings. if (is_signed) { arg_.type = Arg::INT; - arg_.int_value = static_cast(static_cast(value)); + arg_.int_value = static_cast(static_cast(value)); } else { arg_.type = Arg::UINT; - arg_.uint_value = static_cast( - static_cast::Type>(value)); + typedef typename fmt::internal::MakeUnsigned::Type Unsigned; + arg_.uint_value = static_cast(static_cast(value)); } } else { if (is_signed) { @@ -809,7 +824,7 @@ void fmt::internal::PrintfFormatter::format( break; default: --s; - ArgConverter(arg, *s).visit(arg); + ArgConverter(arg, *s).visit(arg); } // Parse type. diff --git a/test/printf-test.cc b/test/printf-test.cc index f7bf375b..12b50cdf 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -289,6 +289,7 @@ SPECIALIZE_MAKE_SIGNED(unsigned, int); SPECIALIZE_MAKE_SIGNED(unsigned long, long); SPECIALIZE_MAKE_SIGNED(fmt::ULongLong, fmt::LongLong); +// Test length format specifier ``length_spec``. template void TestLength(const char *length_spec, U value) { fmt::LongLong signed_value = value; @@ -388,6 +389,13 @@ TEST(PrintfTest, Int) { 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::max(); + EXPECT_PRINTF(fmt::format("{}", max), "%d", max); +} + TEST(PrintfTest, Float) { EXPECT_PRINTF("392.650000", "%f", 392.65); EXPECT_PRINTF("392.650000", "%F", 392.65);