tags:

views:

171

answers:

4

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?

+1  A: 

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.

jpalecek
A: 
//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...

Alsk
A: 

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();
}
Matthieu M.
+2  A: 

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

Hrvoje Prgeša