2

I am working in a section of code with very high performance requirements. I need to perform some formatted string operations, but I am trying to avoid memory allocations, even internal library ones.

In the past, I would have done something similar to the following (assuming C++11):

constexpr int BUFFER_SIZE = 200;
char buffer[BUFFER_SIZE];
int index = 0;
index += snprintf(&buffer[index], BUFFER_SIZE-index, "Part A: %d\n", intA);
index += snprintf(&buffer[index], BUFFER_SIZE-index, "Part B: %d\n", intB);
// etc.

I would prefer to use all C++ methods, such as ostringstream, to do this instead of the old C functions.

I realize I could use std::string::reserve and std::ostringstream to procure space ahead of time, but that will still perform at least one allocation.

Does anyone have any suggestions?

Thanks ahead of time.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
Deon McClung
  • 191
  • 2
  • 8
  • 2
    Write your own [`std::streambuf`](https://en.cppreference.com/w/cpp/io/basic_streambuf) derived class (or find a 3rd party one) that uses a fixed buffer internally, and then you can attach it to a `std::ostream` (not `std::ostringstream`) object via its constructor or `rdbuf()` method – Remy Lebeau Jul 03 '18 at 20:28
  • 1
    I don't think there is anything wrong with 'old C functions', especially since you need high performance. You could even try going to a lower level and use `std::memcpy` + `std::to_chars`, it might be a bit faster. – HolyBlackCat Jul 03 '18 at 20:29
  • If you are doing this for logging purposes, you may want to just go straight for something like spdlog. –  Jul 03 '18 at 20:32
  • You can try fastformat or pantheios (both from Matthew Wilson). Shims are promissing but I never benched them myself. iostream is not intended/designed with speed in mind, I would go for good old snprintf. – Michael Doubez Jul 03 '18 at 22:35

2 Answers2

2

Does anyone have any suggestions?

Yes, use std::ostrstream. I know it is deprecated. But I find it useful for output to static buffers. No possibility of memory leaks if an exception occurs. No allocation of memory at all.

#include <strstream> // for std::ostrstream
#include <ostream>   // for std::ends
// :

constexpr int BUFFER_SIZE = 200;
char buffer[BUFFER_SIZE];
std::ostrstream   osout(buffer, sizeof(buffer));
osout << "Part A: " << intA << "Part B: " << intB << std::ends;
SJHowe
  • 756
  • 5
  • 11
1

My thanks to all that posted suggestions (even in the comments).

I appreciate the suggestion by SJHowe, being the briefest solution to the problem, but one of the things I am looking to do with this attempt is to start coding for the C++ of the future, and not use anything deprecated.

The solution I decided to go with stems from the comment by Remy Lebeau:

#include <iostream>  // For std::ostream and std::streambuf
#include <cstring>   // For std::memset

template <int bufferSize>
class FixedBuffer : public std::streambuf
{
public:
   FixedBuffer()
      : std::streambuf()
   {
      std::memset(buffer, 0, sizeof(buffer));
      setp(buffer, &buffer[bufferSize-1]);         // Remember the -1 to preserve the terminator.
      setg(buffer, buffer, &buffer[bufferSize-1]); // Technically not necessary for an std::ostream.
   }

   std::string get() const
   {
      return buffer;
   }

private:
   char buffer[bufferSize];
};

//...

constexpr int BUFFER_SIZE = 200;
FixedBuffer<BUFFER_SIZE> buffer;
std::ostream ostr(&buffer);

ostr << "PartA: " << intA << std::endl << "PartB: " << intB << std::endl << std::ends;
Deon McClung
  • 191
  • 2
  • 8
  • The get method copies the whole buffer, which is probably not what you want. Also, the resulting string has length `bufferSize` even if the whole buffer was not filled. – Arthur Tacca Apr 14 '20 at 06:39