String Builder: Building Strings with Streams on the Stack

Wednesday Apr 20th 2011 by John Wellbelove
Share:

Discover how to build strings using streams, in one line, using only the stack as storage.

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);
}
Share:
Home
Mobile Site | Full Site
Copyright 2017 © QuinStreet Inc. All Rights Reserved