C++Snippets

String formatting without buffer overrun

This is a complete small program that shows how to allocate a string buffer with the correct size so that a C string formatting routine like sprintf can be used without the fear of a buffer overrun.

The example is mainly useful in a C environment - in C++ you should use stringstream instead of string formatting.

#include <sstream>   // for std::stringstream
#include <cstdarg>   // for variable arguments stuff
#include <cstddef>   // for nullptr
#include <iostream>  // for std::cout

// ----------------------------------------------------------------------

std::stringstream messageStream("");

// ----------------------------------------------------------------------

// Caller must pass a va_list parameter that has been initialized with
// va_start(), and caller must clean up the va_list parameter with va_end().
// This function modifies the va_list parameter.
void WriteFormattedStringToStringStreamArglist(const char* formatString, va_list arglist)
{
  // Perform a "probing" call to vsnprintf(). We do this so that the function
  // tells us how long the resulting string would have been, so that we can
  // allocate a buffer of the correct length. For this we need a copy of
  // arglist, the copy must be made while the original arglist is still in
  // its initialized state.
  va_list arglistCopy;
  va_copy(arglistCopy, arglist);
  int lengthOfFormattedStringWithoutNullTerminator = vsnprintf(nullptr, 0, formatString, arglistCopy);
  va_end(arglistCopy);

  if (lengthOfFormattedStringWithoutNullTerminator >= 0)
  {
    size_t bufferSize = lengthOfFormattedStringWithoutNullTerminator + 1;
    char formattedStringBuffer[bufferSize];

    // No need to call va_start/va-end - that's the responsibility of the caller
    vsnprintf(formattedStringBuffer, bufferSize, formatString, arglist);

    messageStream << formattedStringBuffer;
  }
  else
  {
    messageStream
      << "WriteFormattedStringToStringStreamArglist error: vsnprintf encountered encoding error, return value was "
      << lengthOfFormattedStringWithoutNullTerminator;
  }
}

// ----------------------------------------------------------------------

void WriteFormattedStringToStringStream(const char* formatString, ...)
{
  va_list arglist;

  va_start(arglist, formatString);
  WriteFormattedStringToStringStreamArglist(formatString, arglist);
  va_end(arglist);
}

// ----------------------------------------------------------------------

int main(int argc, char** argv)
{
  const char* formatString = "Hello %s";
  const char* valueString = "world";

  WriteFormattedStringToStringStream(formatString, valueString);

  std::cout << messageStream.str() << std::endl;
}