More C++ Idioms/Polymorphic Exception

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents

[edit]

Polymorphic Exception

[edit] Intent

  • To create an exception object polymorphically
  • To decouple a module from the concrete details of the exceptions it may throw

[edit] Also Known As

[edit] Motivation

Dependency Inversion Principle (DIP), a popular object-oriented software design guideline states that higher level modules should not depend directly on lower level modules. Instead, both should depend on common abstractions (captured in the form of well-defined interfaces). For example, an object of type Person (say John) should not create and use an object of type HondaCivic but instead John should simply commit to a Car interface, which is an abstract base class of HondaCivic. This allows John to upgrade to a Corvette easily in future without any changes to the class Person. John can be "configured" with a concrete instance of a car (any car) using dependency injection technique. Use of DIP leads to flexible and extensible modules that are easy to unit test. Unit testing is simplified by DIP because real objects can be easily replaced with mock objects using dependency injection.

However, there are several occasions when DIP is violated: (1) while using the Singleton pattern and (2) while throwing exceptions! Singleton pattern breaks DIP because it forces the use of the concrete class name while accessing the static instance() function. A singleton should be passed as a parameter while calling a function or a constructor. A similar situation arises while dealing with exceptions in C++. The throw clause in C++ requires a concrete type name (class) to raise an exception. For example,

throw MyConcreteException("Big Bang!");

Any module that throws exceptions like this immediately results into a violation of DIP. Naturally, it is harder to unit test such a module because real exception objects cannot easily be replaced with mock exception objects. A solution like the one below fails miserably as throw in C++ uses static typing and knows nothing about polymorphism.

struct ExceptionBase { };
struct ExceptionDerived : ExceptionBase { };
 
void foo(ExceptionBase& e)
{
   throw e; // Uses static type of e while rasing an exception.
}
int main (void)
{
  ExceptionDerived e;
  try {
    foo(e);
  }
  catch (ExceptionDerived& e) {
    // Exception raised in foo does not match this catch.
  }
  catch (...) {
    // Exception raised in foo is caught here.
  }
}

Polymorphic exception idiom addresses the issue.

[edit] Solution and Sample Code

Polymorphic exception idiom simply delegates the job of raising the exception back to the derived class using a virtual function raise()

struct ExceptionBase 
{ 
  virtual void raise() { throw *this; }
  virtual ~ExceptionBase() {} 
};
struct ExceptionDerived : ExceptionBase 
{ 
  virtual void raise() { throw *this; }
};
 
void foo(ExceptionBase& e)
{
   e.raise(); // Uses dynamic type of e while raising an exception.
}
int main (void)
{
  ExceptionDerived e;
  try {
    foo(e);
  }
  catch (ExceptionDerived& e) {
    // Exception raised in foo now matches this catch.
  }
  catch (...) {
    // not here anymore!
  }
}

The throw statement has been moved into virtual functions. The raise function invoked in function foo is polymorphic and selects the implementation in either ExceptionBase or ExceptionDerived class depending upon what is passed as a parameter (dependency injection). Type of *this is obviously known at compile-time, which results into raising a polymorphic exception. The structure of this idiom is very similar to that of Virtual Constructor idiom.

[edit] Known Uses

[edit] Related Idioms

Virtual Constructor

[edit] References