c++string

Stream object directly into a std::string


Given some type that is streamable:

struct X {
    int i;

    friend std::ostream& operator<<(std::ostream& os, X const& x) {
        return os << "X(" << x.i << ')';
    }
};

I want to append this onto a std::string. I can implement this as:

void append(std::string& s, X const& x) {
    std::ostringstream os;
    os << x;
    s.append(os.str());
}

But this seems lame since I'm writing data into one stream just to then allocate a new string just for the purposes of appending it onto a different one. Is there a more direct route?


Solution

  • This can be solved by a new type of streambuf (see Standard C++ IOStreams and Locales: Advanced Programmer's Guide and Reference).

    Here is a sketch of how it can look:

    #include <streambuf>
    
    class existing_string_buf : public std::streambuf
    {
    public:
        // Store a pointer to to_append.
        explicit existing_string_buf(std::string &to_append); 
    
        virtual int_type overflow (int_type c) {
            // Push here to the string to_append.
        }
    };
    

    Once you flesh out the details here, you could use it as follows:

    #include <iostream>
    
    std::string s;
    // Create a streambuf of the string s
    existing_string_buf b(s);
    // Create an ostream with the streambuf
    std::ostream o(&b);
    

    Now you just write to o, and the result should appear as appended to s.

    // This will append to s
    o << 22;
    

    Edit

    As @rustyx correctly notes, overriding xsputn is required for improving performance.

    Full Example

    The following prints 22:

    #include <streambuf>
    #include <string>
    #include <ostream> 
    #include <iostream>
    
    class existing_string_buf : public std::streambuf
    {
    public:
        // Somehow store a pointer to to_append.
        explicit existing_string_buf(std::string &to_append) : 
            m_to_append(&to_append){}
    
        virtual int_type overflow (int_type c) {
            if (c != EOF) {
                m_to_append->push_back(c);
            }
            return c;
        }
    
        virtual std::streamsize xsputn (const char* s, std::streamsize n) {
            m_to_append->insert(m_to_append->end(), s, s + n);                                                                                 
            return n;
        }
    
    private:
        std::string *m_to_append;
    };
    
    
    int main()
    {   
        std::string s;
        existing_string_buf b(s);
        std::ostream o(&b);
    
        o << 22; 
    
        std::cout << s << std::endl;
    }