From d8754af0639bed3ebdbba6e1fff5057b8ba6e093 Mon Sep 17 00:00:00 2001 From: mojoBrendan Date: Sun, 15 Jan 2017 05:45:48 +1000 Subject: [PATCH] Allow %s as generic format specifier in printf (#453) * Allow %s as generic format specifier in printf Signed integers are formatted as %d Unsigned integers are formatted as %u Doubles are formatted as %f Chars are formatted as %c Void Pointers are formatted as %p * Remove '%S' handling and use visitor for generic format strings * Default for floating point is now "%g" rather than "%f" --- fmt/printf.h | 33 +++++++++++++++++++++++++++++++++ test/printf-test.cc | 10 ++++++++++ 2 files changed, 43 insertions(+) diff --git a/fmt/printf.h b/fmt/printf.h index 28f32f8e..a41d61ba 100644 --- a/fmt/printf.h +++ b/fmt/printf.h @@ -61,6 +61,24 @@ class IsZeroInt : public ArgVisitor { bool visit_any_int(T value) { return value == 0; } }; +// returns the default type for format specific "%s" +class DefaultType : public ArgVisitor { + public: + wchar_t visit_char(int) { return 'c'; } + + wchar_t visit_bool(bool) { return 's'; } + + wchar_t visit_pointer(const void *) { return 'p'; } + + template + wchar_t visit_any_int(T) { return 'd'; } + + template + wchar_t visit_any_double(T) { return 'g'; } + + wchar_t visit_unhandled_arg() { return 's'; } +}; + template struct is_same { enum { value = 0 }; @@ -92,9 +110,18 @@ class ArgConverter : public ArgVisitor, void> { visit_any_int(value); } + void visit_char(char value) { + if (type_ != 's') + visit_any_int(value); + } + template void visit_any_int(U value) { bool is_signed = type_ == 'd' || type_ == 'i'; + if (type_ == 's') { + is_signed = std::numeric_limits::is_signed; + } + using internal::Arg; typedef typename internal::Conditional< is_same::value, U, T>::type TargetType; @@ -463,6 +490,12 @@ void PrintfFormatter::format(BasicCStringRef format_str) { if (!*s) FMT_THROW(FormatError("invalid format string")); spec.type_ = static_cast(*s++); + + if (spec.type_ == 's') { + // set the format type to the default if 's' is specified + spec.type_ = internal::DefaultType().visit(arg); + } + if (arg.type <= Arg::LAST_INTEGER_TYPE) { // Normalize type. switch (spec.type_) { diff --git a/test/printf-test.cc b/test/printf-test.cc index 6bba02e1..4d8dc756 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -202,6 +202,8 @@ TEST(PrintfTest, HashFlag) { TEST(PrintfTest, Width) { EXPECT_PRINTF(" abc", "%5s", "abc"); + EXPECT_PRINTF(" -42", "%5s", "-42"); + EXPECT_PRINTF(" 0.123456", "%10s", 0.123456); // Width cannot be specified twice. EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError, @@ -381,11 +383,13 @@ TEST(PrintfTest, Bool) { TEST(PrintfTest, Int) { EXPECT_PRINTF("-42", "%d", -42); EXPECT_PRINTF("-42", "%i", -42); + EXPECT_PRINTF("-42", "%s", -42); unsigned u = 0 - 42u; EXPECT_PRINTF(fmt::format("{}", u), "%u", -42); EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42); EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42); EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42); + EXPECT_PRINTF(fmt::format("{}", u), "%s", u); } TEST(PrintfTest, LongLong) { @@ -398,6 +402,7 @@ TEST(PrintfTest, LongLong) { TEST(PrintfTest, Float) { EXPECT_PRINTF("392.650000", "%f", 392.65); EXPECT_PRINTF("392.650000", "%F", 392.65); + EXPECT_PRINTF("392.65", "%s", 392.65); char buffer[BUFFER_SIZE]; safe_sprintf(buffer, "%e", 392.65); EXPECT_PRINTF(buffer, "%e", 392.65); @@ -422,6 +427,7 @@ TEST(PrintfTest, Inf) { TEST(PrintfTest, Char) { EXPECT_PRINTF("x", "%c", 'x'); + EXPECT_PRINTF("x", "%s", 'x'); int max = std::numeric_limits::max(); EXPECT_PRINTF(fmt::format("{}", static_cast(max)), "%c", max); //EXPECT_PRINTF("x", "%lc", L'x'); @@ -440,13 +446,17 @@ TEST(PrintfTest, Pointer) { int n; void *p = &n; EXPECT_PRINTF(fmt::format("{}", p), "%p", p); + EXPECT_PRINTF(fmt::format("{}", p), "%s", p); p = 0; EXPECT_PRINTF("(nil)", "%p", p); EXPECT_PRINTF(" (nil)", "%10p", p); + EXPECT_PRINTF("(nil)", "%s", p); + EXPECT_PRINTF(" (nil)", "%10s", p); const char *s = "test"; EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s); const char *null_str = 0; EXPECT_PRINTF("(nil)", "%p", null_str); + EXPECT_PRINTF("(null)", "%s", null_str); } TEST(PrintfTest, Location) {