Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can you wrap one exception inheritance hierarchy into another? --or, another clean way to deal with this?

Tags:

c++

stl

Suppose I have two inheritance hierarchies I'm dealing with in C++. One inherits from std::exception (new hierarchy), and the other inherits from Exception (legacy C++ Builder VCL base exception class). If I call code that may throw either type of exception, I have to write code like this:

try {
    // do stuff....
    Function1();
    Function2();
} catch (std::exception &ex) {
    std::cout << "STL exception caught: " << ex.what() << std::endl;
} catch (Exception &ex) {
    std::cout << "Legacy exception caught: " << ex.Message.c_str() << std::endl;
} catch (SomeOtherVendorLibraryException &ex) {
    // etc.
}

The problem is that each caller needs to have all these catch clauses to try to get every last type of exception, since C++ has no one true, enforced exception base class you can use as a catch-all (e.g. System.Exception class in C#). (catch (...) is a non-starter since you don't have a way of knowing what you caught, and some dangerous system exceptions, like access violations, could be caught as well which are better left untrapped.)

I would like to wrap these "legacy" exceptions into a class in the std::exception hierarchy. This concept of wrapping 3rd-party exceptions into your own exception system isn't entirely unprecedented. For example, the .NET Framework wraps broad sets of errors in other systems (e.g. COMException). Ideally, I'd like to see something like this:

class LegacyException : public std::runtime_error {
public:
    // construct STL exception from legacy exception
    LegacyException(const Exception &ex) : std::runtime_error(ex.Message.c_str()) {}
};

try {
    // In reality, this throw will happen in some function we have no control over.
    throw Exception("Throwing legacy exception!");
} catch (std::exception &ex) {
    // Ideally, the compiler would use the LegacyException constructor
    // to cast the thrown Exception to a LegacyException, which ultimately
    // inherits from std::exception.
    std::cout << ex.what() << std::endl;
}

Understandably, the exception is never caught - it would be asking quite a bit of magic from the compiler for it to catch it.

Is there a solution that might be similar to the above for wrapping a legacy exception, and achieves these goals?

  • One "catch" clause or similar, so that general exception handling logic need only be written once.
  • Logic for converting from one exception type to another must be centralized.
  • Avoids macros if possible.
  • No use of lambda functions.
like image 491
James Johnston Avatar asked Nov 03 '22 01:11

James Johnston


1 Answers

Having worked with BC++Builder I have met the same problem, and macros seemed to be the only solution at the time.

The "cleaner" (hum...) solution is probably a "double-try-catch": the inner try-catch converts your legacy exception into a standard class, and the outer one actually handles the exception.

I don't have the code at hand (it's been years) but basically it boils down to:

#define DTRY  try { try
#define DCATCH catch (Exception& e) { throw LegacyException(e); } } catch

DTRY {
   ...
}
DCATCH(std::exception& e) {
    // handle the exception
}

Yes I know it's ugly, but when I worked with Borland I didn't find anything better. Fact is, at that time Borland was awfully non-standard and I have no idea how it evolved since, maybe you can do better nowadays. Hope this helps anyway.

like image 179
syam Avatar answered Nov 13 '22 02:11

syam