开发者

How to simulate inner exception in C++

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?


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.


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


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.


//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...


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();
}


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜