From b0cde860ae3fee787c582af53858ed03684e845f Mon Sep 17 00:00:00 2001 From: Daniela Engert Date: Tue, 1 Jan 2019 11:45:56 +0100 Subject: [PATCH] Implement 'snprintf(OutputIt it, size_t n, const S &format, const Args & ... args)' (#917) Mostly equivalent to 'sprintf(const S &format, const Args & ... args)' but generates at most 'n' characters through output iterator 'it'. The output type is the same as with 'format_to_n'. Signed-off-by: Daniela Engert --- include/fmt/printf.h | 81 ++++++++++++++++++++++++++++++++++++-------- test/printf-test.cc | 20 +++++++++++ 2 files changed, 87 insertions(+), 14 deletions(-) diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 7894d7f3..d213e84f 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -192,6 +192,13 @@ void printf(basic_buffer& buf, basic_string_view format, basic_format_args args) { Context(std::back_inserter(buf), format, args).format(); } + +template +internal::truncating_iterator printf( + internal::truncating_iterator it, basic_string_view format, + basic_format_args args) { + return Context(it, format, args).format(); +} } // namespace internal using internal::printf; // For printing into memory_buffer. @@ -217,7 +224,8 @@ class printf_arg_formatter typedef typename Range::value_type char_type; typedef decltype(internal::declval().begin()) iterator; typedef internal::arg_formatter_base base; - typedef basic_printf_context context_type; + typedef basic_printf_context + context_type; context_type& context_; @@ -241,11 +249,8 @@ class printf_arg_formatter specifier information for standard argument types. \endrst */ - printf_arg_formatter(internal::basic_buffer& buffer, - format_specs& spec, context_type& ctx) - : base(back_insert_range>(buffer), - &spec, ctx.locale()), - context_(ctx) {} + printf_arg_formatter(iterator iter, format_specs& spec, context_type& ctx) + : base(Range(iter), &spec, ctx.locale()), context_(ctx) {} template typename std::enable_if::value, iterator>::type @@ -378,7 +383,7 @@ class basic_printf_context : using base::parse_context; /** Formats stored arguments and writes the output to the range. */ - void format(); + OutputIt format(); }; template @@ -455,21 +460,21 @@ unsigned basic_printf_context::parse_header( } template -void basic_printf_context::format() { - auto& buffer = internal::get_container(this->out()); +OutputIt basic_printf_context::format() { + auto out = this->out(); const auto range = this->parse_context(); - const Char* end = range.end(); + const Char* const end = range.end(); const Char* start = range.begin(); auto it = start; while (it != end) { char_type c = *it++; if (c != '%') continue; if (it != end && *it == c) { - buffer.append(start, it); + out = std::copy(start, it, out); start = ++it; continue; } - buffer.append(start, it - 1); + out = std::copy(start, it - 1, out); format_specs spec; spec.align_ = ALIGN_RIGHT; @@ -566,9 +571,9 @@ void basic_printf_context::format() { start = it; // Format argument. - visit_format_arg(AF(buffer, spec, *this), arg); + visit_format_arg(AF(out, spec, *this), arg); } - buffer.append(start, it); + return std::copy(start, it, out); } template struct basic_printf_context_t { @@ -583,6 +588,14 @@ typedef basic_printf_context_t::type wprintf_context; typedef basic_format_args printf_args; typedef basic_format_args wprintf_args; +template +struct basic_printf_n_context_t { + typedef fmt::internal::truncating_iterator OutputIter; + typedef output_range Range; + typedef basic_printf_context> + type; +}; + /** \rst Constructs an `~fmt::format_arg_store` object that contains references to @@ -618,6 +631,18 @@ inline std::basic_string vsprintf( return to_string(buffer); } +template +inline typename std::enable_if::value, + format_to_n_result>::type +vsnprintf( + OutputIt out, std::size_t n, const S& format, + basic_format_args::type> + args) { + typedef internal::truncating_iterator It; + auto it = printf(It(out, n), to_string_view(format), args); + return {it.base(), it.count()}; +} + /** \rst Formats arguments and returns the result as a string. @@ -638,6 +663,34 @@ inline FMT_ENABLE_IF_T(internal::is_string::value, return vsprintf(to_string_view(format), basic_format_args(as)); } +/** + \rst + Formats arguments for up to ``n`` characters stored through output iterator + ``out``. The function returns the updated iterator and the untruncated amount + of characters. + + **Example**:: + std::vector out; + + typedef fmt::format_to_n_result< + std::back_insert_iterator>> res; + res Res = fmt::snprintf(std::back_inserter(out), 5, "The answer is %d", 42); + \endrst +*/ +template +inline FMT_ENABLE_IF_T(internal::is_string::value&& + internal::is_output_iterator::value, + format_to_n_result) + snprintf(OutputIt out, std::size_t n, const S& format, + const Args&... args) { + internal::check_format_string(format); + typedef FMT_CHAR(S) Char; + typedef typename basic_printf_n_context_t::type context; + format_arg_store as{args...}; + return vsnprintf(out, n, to_string_view(format), + basic_format_args(as)); +} + template inline int vfprintf( std::FILE* f, const S& format, diff --git a/test/printf-test.cc b/test/printf-test.cc index e755d537..8cfe5205 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -555,3 +555,23 @@ TEST(PrintfTest, VSPrintfMakeWArgsExample) { fmt::make_wprintf_args(42, L"something"))); #endif } + +TEST(PrintfTest, snprintf) { + char buffer[4] = "xxx"; + auto result = fmt::snprintf(buffer, 0, "test"); + EXPECT_EQ("xxx", fmt::to_string_view(buffer)); + EXPECT_EQ(4u, result.size); + EXPECT_EQ(buffer, result.out); + result = fmt::snprintf(buffer, 2, "test"); + EXPECT_EQ("tex", fmt::to_string_view(buffer)); + EXPECT_EQ(4u, result.size); + EXPECT_EQ(buffer + 2, result.out); + result = fmt::snprintf(buffer, 3, "test"); + EXPECT_EQ("tes", fmt::to_string_view(buffer)); + EXPECT_EQ(4u, result.size); + EXPECT_EQ(buffer + 3, result.out); + result = fmt::snprintf(buffer, 4, "test"); + EXPECT_EQ("test", std::string(buffer, 4)); + EXPECT_EQ(4u, result.size); + EXPECT_EQ(buffer + 4, result.out); +}