String Builder: Building Strings with Streams on the Stack

I had a requirement for building strings for logging, but the std::ostringstream was
less than optimum. Firstly, it required more than one line of code to build the string and
secondly, it uses the heap for buffering. The code here describes a simple, stack based, string builder class that
that uses a fixed array as the workspace for a string stream.

How the String Builder Works

The string builder works by creating a class derived from streambuf (Stream_Buffer) that has its
buffer set to a fixed internal array, and using that as the buffer for the ostream derived object (Stream).
The Stream_Buffer discards any characters that would cause the buffer to overflow. An overloaded << operator
in the String_Builder class adds objects to the internal stream. A conversion operator to
std::string is provided as well as two functions ‘str’ and ‘c_str’ to convert to std::string
and const char*.

The default size for the buffer is set to 256 characters. Larger buffers can be
accommodated by using a larger template parameter.

Example of the String Builder


void Log(const std::string &text)
{
    // Log the text somewhere...
}

int value  = 10;
Log(String_Builder<>() << "The value is " << value << "n");

Log(String_Builder<1000>() << "A really long string up to 1000 characters");

The String Builder Code

Following is the code for thie String_Builder class:

#include <ostream>
#include <streambuf>

template <const int MAX_CHARACTERS = 256>
class String_Builder
{
private:

    //*****************************************************************
    // Special stream buffer
    //*****************************************************************
    class Stream_Buffer : public std::streambuf
    {
    public:
        //**************************************************************
        // Constructor
        //**************************************************************
        Stream_Buffer()
        {
            // Set the pointer to the internal buffer.
            setp(buffer, buffer + MAX_CHARACTERS);
        }

        //**************************************************************
        // Returns a null terminated string.
        //**************************************************************
        const char *c_str() const
        {
            *pptr() = 0;

            return (pbase());
        }

    protected:

        //**************************************************************
        // Overridden Overflow()
        //**************************************************************
        int_type overflow(int_type c = 0)
        {
            // Don't do anything if we have no more room.
            return (0);
        }

    private:

        // The buffer that accumulates the characters.
        // Leave room for a terminating zero.
        char buffer[MAX_CHARACTERS + 1];
    };

    //*****************************************************************
    // Special stream
    //*****************************************************************
    class Stream : public std::ostream
    {
    public:

        //**************************************************************
        // Constructor
        //**************************************************************
        Stream()
            : std::ostream(&stream_buffer)
        {
        }

        //**************************************************************
        // Returns a null terminated string.
        //**************************************************************
        const char *c_str() const
        {
            return (stream_buffer.c_str());
        }

    private:

        Stream_Buffer stream_buffer; // The stream that builds the string.
    };

public:

    //*********************************************************************
    // Output stream operator.
    //*********************************************************************
    template<typename T>
    String_Builder &operator <<(const T &value)
    {
        builder << value;

        return (*this);
    }

    //*********************************************************************
    // Conversion operator to std::string
    //*********************************************************************
    operator std::string() const
    {
        return (std::string(builder.c_str()));
    }

    //*********************************************************************
    // Conversion to std::string
    //*********************************************************************
    std::string str() const
    {
        return (std::string(builder.c_str()));
    }

    //*********************************************************************
    // Pointer to the internal C string.
    //*********************************************************************
    const char *c_str() const
    {
        return (builder.c_str());
    }

private:

    Stream builder; // The stream used to accumulate text.
};

//*********************************************************************
// Operator to stream to a std::ostream.
//*********************************************************************
template <const int MAX_CHARACTERS>
inline std::ostream &operator <<(std::ostream &os, const String_Builder<MAX_CHARACTERS> &sb)
{
    os << std::string(sb);

    return (os);
}

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read