olq_plo
olq_plo

Reputation: 1242

iostream equivalent to snprintf(NULL, 0, format_string, args...)

I want to find the number of characters that a stream formatting operation would produce without allocating memory from the heap. In C, it can be done with

int nchars = snprintf(NULL, 0, format_string, args...);

How can it be done within the ostream framework in C++?

An implementation with std::ostringstream may allocate memory from the heap:

template <class T>
int find_nchar(const T& value) {
  std::ostringstream os; // may allocate memory from the heap
  os << value;
  return os.str().size(); // may allocate memory from the heap
}

I think I need to make a custom ostream class to achieve this. The custom ostream should respect all the formatting flags one can set for the normal std::ostream.

I am searching for a solution that only uses the C++ standard library, not boost::iostreams, for example.

Upvotes: 2

Views: 220

Answers (1)

G.M.
G.M.

Reputation: 12899

Rather than a custom std::ostream it might be easier -- and perhaps more flexible -- to implement a custom std::streambuf that can then be used with any std::ostream.

#include <streambuf>

template <class CharT, class Traits = std::char_traits<CharT>>
struct counting_streambuf: std::basic_streambuf<CharT, Traits> {
  using base_t = std::basic_streambuf<CharT, Traits>;
  using typename base_t::char_type;
  using typename base_t::int_type;

  std::streamsize count = 0;

  std::streamsize xsputn(const char_type* /* unused */, std::streamsize n)
    override
    {
      count += n;
      return n;
    }

  int_type overflow(int_type ch)
    override
    {
      ++count;
      return ch;
    }

};

Then use as...

#include <iostream>

int
main (int argc, char **argv)
{
  using char_type = decltype(std::cout)::char_type;

  counting_streambuf<char_type> csb;

  /*
   * Associate the counting_streambuf with std::cout whilst
   * retaining a pointer to the original std::streambuf.
   */
  auto *oldbuf = std::cout.rdbuf(&csb);
  std::cout << "Some text goes here...\n";

  /*
   * Restore the original std::streambuf.
   */
  std::cout.rdbuf(oldbuf);
  std::cout << "output length is " << csb.count << " characters\n";
}

Running the above results in...

output length is 23 characters

Edit: The original solution didn't overload overflow. This works on Linux but not on Windows. Thanks go to Peter Dimov from Boost, who found the solution.

Upvotes: 2

Related Questions