Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper/elegant way of implementing C++ exception chaining?

Tags:

c++

I'd like to implement an Exception class in C++ that mimics the one from .NET framework (and Java has something similar too), for the following purposes:

  1. Exception chaining: I'd like to implement the concept of "exception translation", when exceptions caught at higher levels wrap and "translate" the lower level exceptions, also preserving these lower-level exceptions somehow (in the InnerException member, in this case). For this, there should be some mechanism to store inner exceptions along with each exception thrown at the upper level. InnerException member provides this in the implementation below.

  2. Exception inheritance: there should be possible to derive IoException from Exception, and SerialPortException from IoException, for example. While this seems trivial, there should be ability to identify the type of caught exceptions dynamically (e.g. for logging purposes, or to display to user), preferably without the overhead of RTTI and typeid.

This is the sample exception handling logic I'd like to make possible:

try
{
    try
    {
        try
        {
            throw ThirdException(L"this should be ThirdException");
        }
        catch(Exception &ex)
        {
            throw SubException(L"this should be SubException", ex);
        }
    }
    catch(Exception &ex)
    {
        throw SubException(L"this should be SubException again", ex);
    }
}
catch(Exception &ex)
{
    throw Exception(L"and this should be Exception", ex);
}

and when catching the "outer-most" exception in the upmost layer I'd like to be able to parse and format whole exception chain through the InnerException member, to display something like this:

Exception chain formatting

I've come up with the following implementation so far:

Small note: CString is Microsoft-specific string class (just for the people not familiar with Visual C++ stuff).

class Exception
{
protected:

    Exception(const Exception&) {};
    Exception& operator= (const Exception&) {};

public:

    Exception(const CString &message) : InnerException(0), Message(message) {}
    Exception(const CString &message, const Exception &innerException) : InnerException(innerException.Clone()), Message(message) {}

    virtual CString GetExceptionName() const { return L"Exception"; }

    virtual Exception *Clone() const
    {
        Exception *ex = new Exception(this->Message);
        ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
        return ex;
    }

public:

    virtual ~Exception() { if (InnerException) delete InnerException; }

    CString Message;
    const Exception *InnerException;
};

Now what do we have here. Copy constructor and assignment operator are made protected to prevent copying. Each object will "own" its inner exception object (and delete it in destructor), so default shallow-copying would be unacceptable. Then we have two pretty standard-looking constructors and virtual destructor that deletes the InnerException object. Clone() virtual method is responsible for deep-copying the objects, primarily for storing the inner exception object (see the second constructor). And finally GetExceptionName() virtual method provides the cheap alternative to RTTI for identification of exception class names (I don't think this looks cool but I couldn't come up with better solution; for comparison: in .NET one could simply use someException.GetType().Name).

Now this does the job. But... I don't like this solution for one particular reason: the amount of coding needed for each derived class. Consider I have to derive SubException class, which provides absolutely zero additions to the base class functionality, it just provides the custom name ("SubException", which might be "IoException", "ProjectException", ...) to differentiate it for its usage scenario. I have to provide almost same amount of code for each of such exception class. Here it is:

class SubException : public Exception
{
protected:

    SubException(const SubException& source) : Exception(source) {};
    SubException& operator= (const SubException&) {};

public:

    SubException(const CString &message) : Exception(message) {};
    SubException(const CString &message, const Exception &innerException) : Exception(message, innerException) {};

    virtual CString GetExceptionName() const { return L"SubException"; }

    virtual Exception *Clone() const
    {
        SubException *ex = new SubException(this->Message);
        ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
        return ex;
    }
};

I don't like the fact that I have to provide protected copy constructor and assignment operator each time, I don't like the fact that I have to clone the Clone method each time, duplicating even the code of copying the base members (InnerException...), simply... I don't think this is the elegant solution. But I was unable to think of better one. Do you have any ideas how to implement this concept "properly"? Or maybe this is the best implementation of this concept that is possible in C++? Or maybe I'm doing this completely wrong?

P.S.: I know there exist some mechanisms in C++11 (also in Boost) for this purpose (exception chaining) with some new exception classes, but I'm primarily interested in custom, "old-C++-compatible" ways. But it would be good, in addition, if someone could provide any code in C++11 that accomplishes the same.

like image 337
TX_ Avatar asked Nov 13 '12 07:11

TX_


People also ask

How do you implement top level exception handling in C++?

Exception Handling in C++ C++ provides the following specialized keywords for this purpose: try: Represents a block of code that can throw an exception. catch: Represents a block of code that is executed when a particular exception is thrown. throw: Used to throw an exception.

How exception handling is implemented in the C++ program?

Exception handling in C++ consist of three keywords: try , throw and catch : The try statement allows you to define a block of code to be tested for errors while it is being executed. The throw keyword throws an exception when a problem is detected, which lets us create a custom error.

What is the meaning of exception chaining?

Exception chaining, or exception wrapping, is an object-oriented programming technique of handling exceptions by re-throwing a caught exception after wrapping it inside a new exception. The original exception is saved as a property (such as cause) of the new exception.

How do you handle exceptions in method chaining in Java?

Methods Of Throwable class Which support chained exceptions in java : getCause() method :- This method returns actual cause of an exception. initCause(Throwable cause) method :- This method sets the cause for the calling exception.


2 Answers

C++11 already has nested_exception. There was a talk about exceptions in C++03 and C++11 at Boostcon/C++Next 2012. Videos are on youtube:

  1. http://www.youtube.com/watch?v=N9bR0ztmmEQ&feature=plcp
  2. http://www.youtube.com/watch?v=UiZfODgB-Oc&feature=plcp
like image 174
Juraj Blaho Avatar answered Sep 29 '22 04:09

Juraj Blaho


There is a lot of extra code, but the good thing is it's really EASY extra code that doesn't change at all from class to class, so it's possible to preprocessor macro it.

#define SUB_EXCEPTION(ClassName, BaseName) \
  class ClassName : public BaseName\
  {\
  protected:\
  \
      ClassName(const ClassName& source) : BaseName(source) {};\
      ClassName& operator= (const ClassName&) {};\
  \
  public:\
  \
      ClassName(const CString &message) : BaseName(message) {};\
      ClassName(const CString &message, const BaseName &innerException) : BaseName(message, innerException) {};\
  \
      virtual CString GetExceptionName() const { return L"ClassName"; }\
  \
      virtual BaseName *Clone() const\
      {\
          ClassName *ex = new ClassName(this->Message);\
          ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;\
          return ex;\
      }\
  };

Then you can define various utility exceptions by just doing:

SUB_EXCEPTION(IoException, Exception);
SUB_EXCEPTION(SerialPortException, IoException);
like image 45
OmnipotentEntity Avatar answered Sep 29 '22 06:09

OmnipotentEntity