8

Basically I want to simulate .NET Exception.InnerException in C++. I want to catch exception from bottom layer and wrap it with another exception and throw again to upper layer. The problem here is I don't know how to wrap the catched exception inside another exception.

struct base_exception : public std::exception
{
    std::exception& InnerException;

    base_exception() : InnerException(???) { } // <---- what to initialize with
    base_exception(std::exception& innerException) : InnerException(innerException) { }
};

struct func1_exception : public base_exception 
{
    const char* what() const throw()
    {
        return "func1 exception";
    }
};

struct func2_exception : public base_exception
{
    const char* what() const throw()
    {
        return "func2 exception";
    }
};

void func2()
{
    throw func2_exception();
}

void func1()
{
    try
    {
        func2();
    }
    catch(std::exception& e)
    {
        throw func2_exception(e); // <--- is this correct? will the temporary object will be alive?
    }
}

int main(void)
{
    try
    {
        func1();
    }
    catch(base_exception& e)
    {
        std::cout << "Got exception" << std::endl;
        std::cout << e.what();
        std::cout << "InnerException" << std::endl;
        std::cout << e.InnerException.what(); // <---- how to make sure it has inner exception ?
    }
}

In the above code listing I am not sure how to initialize the "InnerException" member when there is no inner exception. Also I am not sure whether the temporary object that is thrown from func1 will survive even after func2 throw?

3
  • Not really an answer, but you may be interested in the Boost.Exception approach: boost.org/doc/libs/release/libs/exception/index.html Commented May 14, 2010 at 18:45
  • I don't understand why would function catch exception if it doesn't know what to do with it. Just to collect call stack? Commented May 14, 2010 at 19:05
  • @Alsk: Good question. Typically you want to catch an exception where you can best handle it, and then deal with it there. I can understand intercepting it to log it, but then you'd think a plain throw; would be best. I'd really like to see a use case for when you'd want to wrap an exception. Commented May 14, 2010 at 19:36

6 Answers 6

4

Since C++ 11 you have new options:

  1. You can use std::exception_ptr.

    The exception is then preserve until last exception_ptr to this exception is destroyed.

    struct base_exception : public std::exception
    {
        std::exception_ptr InnerException;
    
        base_exception() {}
        base_exception(std::exception& innerException)
         : InnerException(std::make_exception_ptr(innerException))
        {}
    
    };
    
  2. Or you can simply use std::nested_exception.
Sign up to request clarification or add additional context in comments.

Comments

3

You should also take a look at boost exception for an alternative solution to wrapping.

Comments

1

Also I am not sure whether the temporary object that is thrown from func1 will survive even after func2 throw?

No. Unless you rethrow the exception with throw;. You could implement this if you'd allow only some (limited) set of exception types.

Comments

0
//inversion of the problem :)
struct base_exception : public std::exception
{
    std::list<base_exception*> snowball;

    base_exception() { }
    void add(base_exception* e) { snowball.push_back(e); }
};

void func2()
{
    func2_exception e;
    e.add(new func2_exception());
    throw e;
}

void func1()
{
    try
    {
        func2();
    }
    catch(base_exception& e)
    {
        e.add(new func1_exception());
        throw e; 
    }
}
int main(void)
{
    try
    {
        func1();
    }
    catch(base_exception& e)
    {
        std::cout << "Got exception" << std::endl;
        //print info in the direct order of exceptions occurence
        foreach(base_exception* exception, e.snowball)
        {
              std::cout << exception->what();
              std::cout << "next exception was:" << std::endl;
        }
    }
}

hmmmm...

Comments

0

One problem with the inner exception is the possibility to throw it again while maintaining polymorphic behaviour.

This can be (somewhat) alleviate by actually managing the exception lifetime yourself and providing polymorphic copies.

// Base class
class exception: virtual public std::exception, private boost::noncopyable
{
public:
  virtual exception* clone() const = 0;
  virtual void rethrow() const = 0; // throw most Derived copy
};

// ExceptionPointer
class ExceptionPointer: virtual public std::exception
{
public:
  typedef std::unique_ptr<exception> pointer;

  ExceptionPointer(): mPointer() {}
  ExceptionPointer(exception* p): mPointer(p) {}
  ExceptionPointer(pointer p): mPointer(p) {}

  exception* get() const { return mPointer.get(); }
  void throwInner() const { if (mPointer.get()) mPointer->rethrow(); }

  virtual char* what() const { return mPointer.get() ? mPointer->what() : 0; }

private:
  pointer mPointer;
};

How to use ?

try
{
  // some code
}
catch(exception& e)
{
  throw ExceptionPointer(e.clone());
}

// later on
try
{
}
catch(ExceptionPointer& e)
{
  e.throwInner();
}

Comments

0

As stated by others, boost::exception is a nice option. However, like all options that use a common base class approach, they rely on all thrown exceptions being derived from that base class. If your intermediary catch handlers need to add information to an exception from a third party library it won't work.

An option that might be sufficient is to have intermediary catch handlers like this:

catch (std::exception& ex)
{
   std::string msg = ex.what();
   msg.append(" - my extra info");
   ex = std::exception(msg.c_str()); // slicing assignment
   throw;                            // re-throws 'ex', preserving it's original type
}

This only works for implementations of std::exception that provide a constructor taking a string parameter (e.g. VC++). The std::exception constructor taking a string is a non-standard extension.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.