diff --git a/include/fmt/format.h b/include/fmt/format.h index ed1bcf38..f10018be 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -702,7 +702,7 @@ FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator it) { template class counting_iterator { private: - std::size_t& count_; + std::size_t* count_; mutable T blackhole_; public: @@ -712,16 +712,10 @@ class counting_iterator { typedef T* pointer; typedef T& reference; - explicit counting_iterator(std::size_t &count): count_(count) {} - counting_iterator(const counting_iterator &other): count_(other.count_) {} - - counting_iterator& operator=(const counting_iterator &other) { - count_ = other.count_; - return *this; - } + explicit counting_iterator(std::size_t &count): count_(&count) {} counting_iterator& operator++() { - ++count_; + ++*count_; return *this; } @@ -730,6 +724,41 @@ class counting_iterator { T &operator*() const { return blackhole_; } }; +// An output iterator that truncates the output and counts the number of objects +// written to it. +template +class truncating_iterator { + private: + typedef std::iterator_traits traits; + + OutputIt out_; + std::size_t limit_; + std::size_t *count_; + mutable typename traits::value_type blackhole_; + + public: + typedef std::output_iterator_tag iterator_category; + typedef typename traits::value_type value_type; + typedef typename traits::difference_type difference_type; + typedef typename traits::pointer pointer; + typedef typename traits::reference reference; + + truncating_iterator(OutputIt out, std::size_t limit, std::size_t &count) + : out_(out), limit_(limit), count_(&count) {} + + OutputIt base() const { return out_; } + + truncating_iterator& operator++() { + if ((*count_)++ < limit_) + ++out_; + return *this; + } + + truncating_iterator operator++(int) { return ++*this; } + + reference operator*() const { return *count_ < limit_ ? *out_ : blackhole_; } +}; + // Returns true if value is negative, false otherwise. // Same as (value < 0) but doesn't produce warnings if T is an unsigned type. template @@ -3374,6 +3403,22 @@ inline typename std::enable_if< return vformat_to(out, format_str, make_args(args...)); } +template +struct format_to_n_result { + OutputIt out; + std::size_t size; +}; + +template +inline format_to_n_result format_to_n( + OutputIt out, std::size_t n, string_view format_str, const Args & ... args) { + typedef internal::truncating_iterator It; + std::size_t count = 0; + auto it = vformat_to(It(out, n, count), format_str, + *make_args::type>(args...)); + return {it.base(), count}; +} + inline std::string vformat(string_view format_str, format_args args) { memory_buffer buffer; vformat_to(buffer, format_str, args); diff --git a/test/format-test.cc b/test/format-test.cc index a81020fa..cb1cde27 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1505,6 +1505,14 @@ TEST(FormatTest, OutputSize) { EXPECT_EQ(2u, fmt::count("{}", 42)); } +TEST(FormatTest, FormatToN) { + char buffer[4]; + buffer[3] = 'x'; + auto result = fmt::format_to_n(buffer, 3, "{}", 12345); + EXPECT_EQ(5u, result.size); + EXPECT_EQ("123x", fmt::string_view(buffer, 4)); +} + #if FMT_USE_CONSTEXPR struct test_arg_id_handler { enum result { NONE, EMPTY, INDEX, NAME, ERROR };