diff --git a/include/fmt/printf.h b/include/fmt/printf.h index f80a57ad240fd..2384fa2af59b3 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -141,6 +141,13 @@ template class char_converter { void operator()(T) {} // No conversion needed for non-integral types. }; +// An argument visitor that return a pointer to a C string if argument is a +// string or null otherwise. +template struct get_cstring { + template const Char* operator()(T value) { return nullptr; } + const Char* operator()(const Char* s) { return s; } +}; + // Checks if an argument is a valid printf width specifier and sets // left alignment if it is negative. template class printf_width_handler { @@ -561,6 +568,13 @@ OutputIt basic_printf_context::format() { break; } } + if (specs.precision >= 0 && arg.type() == internal::type::cstring_type) { + auto str = visit_format_arg(internal::get_cstring(), arg); + auto end = str + specs.precision; + auto nul = std::find(str, end, Char()); + arg = internal::make_arg(basic_string_view( + str, nul != end ? nul - str : specs.precision)); + } start = it; diff --git a/test/printf-test.cc b/test/printf-test.cc index adb7e65a11879..7873c28d66699 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -259,6 +259,11 @@ TEST(PrintfTest, FloatPrecision) { EXPECT_PRINTF(buffer, "%.3a", 1234.5678); } +TEST(PrintfTest, StringPrecision) { + char test[] = {'H', 'e', 'l', 'l', 'o'}; + EXPECT_EQ(fmt::sprintf("%.4s", test), "Hell"); +} + TEST(PrintfTest, IgnorePrecisionForNonNumericArg) { EXPECT_PRINTF("abc", "%.5s", "abc"); }