Search code examples
c++exceptioniostreamostream

Can I throw a stream?


I'm trying to develop an Exception class, that allows collecting relevant data stream-style.

Following Custom stream to method in C++? I've extended my own class:

class NetworkException : public std::exception, public std::ostream

to pick the error data from a stream and then return whatever it acquired through .what().

Then I tried something like this:

try {
    ssize_t ret = send(sock, headBuffer, headLength, MSG_MORE);

    if (ret <= 0)  throw NetworkException() << "Error sending the header: " << strerror(errno);

    // much more communication code

} catch (NetworkException& e) {
    connectionOK = false;
    logger.Warn("Communication failed: %s",e.what());
}

But the compiler produces an error:

HTTPClient.cpp:246:113: error: use of deleted function 'std::basic_ostream<char>::basic_ostream(const std::basic_ostream<char>&)'

(that's the line with the throw.)

I realize streams don't have copy constructors, but I thought catching a reference instead of the object would suffice. How can I overcome this - can I throw an object that 'swallows' a stream?


Solution

  • You cannot copy object containing stream object using default copy constructor, but you can write your own copy constructor that will copy content of stream.

    #include <iostream>
    #include <sstream>
    
    struct StreamableError : public std::exception {
            template <typename T>
            StreamableError& operator << (T rhs) {
                innerStream << rhs;
                return *this;
            }
    
            StreamableError() = default;
    
            StreamableError(StreamableError& rhs) {
                    innerStream << rhs.innerStream.str();
            }
    
            virtual const char* what() const noexcept {
                str = innerStream.str();  //this can throw
                return str.c_str();
            }
    
        private:
            std::stringstream innerStream;
            mutable std::string str;
    };
    
    
    int main() {
            try {
                    throw StreamableError() << "Formatted " << 1 << " exception.";
            } catch (std::exception& e) {
                    std::cout << e.what() << std::endl;
            }
    }
    

    Solution above is not perfect. If str = innerStream.str() throws then std::terminate will be called. If you want to make what method to be trully noexcept then you have two choices:

    1. Copy content of stream to str variable inside copy constructor. Then you will not be able to call what method to obtain exception message before you throw.
    2. Copy content of stream to str variable inside copy constructor and << operator. In this case you will be able to obtain exception message before throw, but you will be copying message each time operator is called, which may be an overkill.