From f4156b57f0c5d6fdcf109a28dbbc27e1035fd5c1 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 30 Jul 2014 08:39:07 -0700 Subject: [PATCH] Implement 'hh' length specifier in printf. --- format.cc | 33 ++++++++++++++++++++++----------- format.h | 6 +++--- test/printf-test.cc | 10 ++++++---- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/format.cc b/format.cc index f4c678d6..c040edd1 100644 --- a/format.cc +++ b/format.cc @@ -207,23 +207,34 @@ class PrecisionHandler : } }; +// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. +template +struct MakeUnsigned { typedef T Type; }; + +template <> +struct MakeUnsigned { typedef unsigned char Type; }; + +template <> +struct MakeUnsigned { typedef unsigned short Type; }; + // Converts an integer argument to type T. template class ArgConverter : public fmt::internal::ArgVisitor, void> { private: fmt::internal::Arg &arg_; + char type_; public: - explicit ArgConverter(fmt::internal::Arg &arg) : arg_(arg) {} + ArgConverter(fmt::internal::Arg &arg, char type) : arg_(arg), type_(type) {} template void visit_any_int(U value) { - if (std::numeric_limits::is_signed) { + if (type_ == 'd' || type_ == 'i') { arg_.type = fmt::internal::Arg::INT; arg_.int_value = static_cast(value); } else { arg_.type = fmt::internal::Arg::UINT; - arg_.uint_value = static_cast(value); + arg_.uint_value = static_cast::Type>(value); } } }; @@ -890,16 +901,14 @@ void fmt::internal::PrintfFormatter::format( spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. } - // Parse length. + // Parse length and convert the argument to the required type. switch (*s) { case 'h': { ++s; - // TODO: handle 'hh' - char type = *s; - if (type == 'd' || type == 'i') - ArgConverter(arg).visit(arg); + if (*s == 'h') + ArgConverter(arg, *++s).visit(arg); else - ArgConverter(arg).visit(arg); + ArgConverter(arg, *s).visit(arg); break; } case 'l': @@ -918,8 +927,10 @@ void fmt::internal::PrintfFormatter::format( if (error_) throw FormatError(error_); spec.type_ = static_cast(*s++); - if (arg.type <= Arg::LAST_INTEGER_TYPE && spec.type_ == 'u') - spec.type_ = 'd'; + if (arg.type <= Arg::LAST_INTEGER_TYPE && + (spec.type_ == 'i' || spec.type_ == 'u')) { + spec.type_ = 'd'; + } start = s; diff --git a/format.h b/format.h index 3674ac2d..bfaa1249 100644 --- a/format.h +++ b/format.h @@ -740,6 +740,9 @@ class ArgVisitor { Result visit_ulong_long(ULongLong value) { return FMT_DISPATCH(visit_any_int(value)); } + Result visit_char(int value) { + return FMT_DISPATCH(visit_any_int(value)); + } template Result visit_any_int(T) { return FMT_DISPATCH(visit_unhandled_arg()); @@ -756,9 +759,6 @@ class ArgVisitor { return FMT_DISPATCH(visit_unhandled_arg()); } - Result visit_char(int) { - return FMT_DISPATCH(visit_unhandled_arg()); - } Result visit_string(Arg::StringValue) { return FMT_DISPATCH(visit_unhandled_arg()); } diff --git a/test/printf-test.cc b/test/printf-test.cc index a53788e2..ea76441d 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -274,17 +274,17 @@ TEST(PrintfTest, DynamicPrecision) { #define EXPECT_STD_PRINTF(format, arg) { \ char buffer[BUFFER_SIZE]; \ - safe_sprintf(buffer, "%hd", arg); \ - EXPECT_PRINTF(buffer, "%hd", arg); \ + safe_sprintf(buffer, fmt::StringRef(format).c_str(), arg); \ + EXPECT_PRINTF(buffer, format, arg); \ } template void TestLength(const char *length_spec) { - EXPECT_STD_PRINTF(format, 42); T min = std::numeric_limits::min(), max = std::numeric_limits::max(); const char types[] = {'d', 'i', 'u', 'o', 'x', 'X'}; for (int i = 0; i < sizeof(types); ++i) { std::string format = fmt::format("%{}{}", length_spec, types[i]); + //EXPECT_STD_PRINTF(format, 42); EXPECT_STD_PRINTF(format, min); EXPECT_STD_PRINTF(format, max); EXPECT_STD_PRINTF(format, fmt::LongLong(min) - 1); @@ -300,9 +300,11 @@ void TestLength(const char *length_spec) { } } -TEST(PrintfTest, ShortLength) { +TEST(PrintfTest, Length) { TestLength("h"); TestLength("h"); + TestLength("hh"); + TestLength("hh"); // TODO: more tests }