6
votes

I want to create a number of exception types which derive from std::runtime_error and I want them to have stringstream type functionality. I've therefore created an exception class which composes a std::stringstream and which derives from std::runtime_error:

template<typename T>
class error_stream : public std::runtime_error
{
public:
      error_stream() :
      std::runtime_error(""), ss(std::make_shared<std::basic_stringstream<T>> ())
      {
      }

      ~error_stream() throw()
      {
      }

      template <typename T>
      error_stream & operator << (const T & t)
      {
          *ss << t;
          return *this;
      }

      virtual const char * what() const throw()
      {
          get_str(s_, ss->str());
          return s_.c_str();
      }

protected:

    template <typename T>
    void get_str(std::basic_string<char> & s_, const std::basic_string<T> & str) const
    {
        s_ = str;
    }

    template<>
    void get_str(std::basic_string<char> & s_, const std::basic_string<wchar_t> & str) const
    {
        std::basic_string<char> ret(str.begin(), str.end());
        s_ = ret;
    }

protected:
    std::shared_ptr<std::basic_stringstream<T>> ss;
    mutable std::basic_string<char> s_;
};

And I've created a more specific exception type which in turn derives from this error_stream exception:

template<typename T>
class w32file_exception : public w32utils::error_stream<T> 
{
public:
    w32file_exception() : error_stream<T>() {}
};

However, I've encountered something I don't understand here, because when I throw a w32file_exception I actually can only catch it as it's parent error_stream. Can anyone see what I'm doing wrong?

    try
    {
        throw w32file_exception<char>() << "test";
    }
    catch ( w32file_exception<char> & e )
    {
        ASSERT_PASSED;
    }
    catch ( error_stream<char> & e )
    {
        std::cout << e.what() << std::endl;  // Why do I end up here?
    }
1
I don't know your use case of the operator<< so I don't know if this helps - but I just wanted to mention Boost.Exception and in particular the Transporting of Arbitrary Data to the Catch Site - Florian Sowade

1 Answers

13
votes

What exactly does your throw look like? Are you using your operator<< before calling throw, like this:

throw w32file_exception<T>() << "fooobar";

Then the answer is, that your operator<< returns an error_stream and no w32file_exception and so the type of the thrown exception is error_stream.

You could solve this problem this way:

template<typename T, typename DERIVED>
    class error_stream : public std::runtime_error
{
public:
    // ...
    template <typename U>
        DERIVED & operator << (const T & t)
    {
        *ss << t;
        return static_cast<DERIVED&>(*this);
    }
    // ...
};

But then you loose the ability to catch every error_stream exception because it is a new Type for every DERIVED type.