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);
}


About the Author

John Wellbelove

John has been in the electronics business for 30 years, gradually moving over the years from pure hardware design to mostly software with hardware interface elements in a real-time environment. Over the years he has programmed in Assembler (6502, 6809, 68000), BASIC, Pascal, C & C++. He has dabbled a little in PHP/MySQL as he runs run a climbing club website and forum. http://www.southamptonrats.org. Projects have included DCT based image compression (pre-jpeg), remote imaging security systems, CCTV cameras, images analysis, real-time conveyor control. He is currently working on a template library based on the STL for images and image algorithms.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Companies undertaking an IT project need to find the right balance between cost and functionality. It's important to start by determining whether to build a solution from scratch, buy an out-of-the-box solution, or a combination of both. In reality, most projects will require some system tailoring to meet business requirements. Decision-makers must understand how much software development is enough and craft a detailed implementation plan to ensure the project's success. This white paper examines the different …

  • The mobile market is white hot. Building a well-crafted product development plan that addresses market research, strategy, design, and development will provide the greatest chance for success. Each phase of an app's lifecycle is critical to the its overall success and feeds into the next step of the process of product development for the app. This white paper examines the five key phases of mobile app creation, one by one, to understand how they work together to help create a successful mobile app.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date