mardi 24 octobre 2023

Catching a Diamond Of Death Exception (boost::system::system_error and std::system_error)

I have a library, and I wish to leverage the design of boost::system::system_error. C++ adopted this design in 2011 with std::system_error. I would like for client code to have the option of working with Boost exceptions or with Standard exceptions. Therefore, when my library code needs to communicate an error, I throw my own exception type that inherits from boost::system::system_error and std::system_error:

class my_exception : public std::system_error, public boost::system::system_error {
  // implementation
};

Unfortunately, this is a Deadly Diamond of Death with respect to the common base class std::runtime_error. Nonetheless, client code is able to catch these exceptions as either std::system_error or as boost::system::system_error:

try {
  a_function_that_might_throw_my_exception();
} catch (const std::system_error& ex) {
  // Works as intended - correctly catches a reference to the `my_exception` object
}

try {
  a_function_that_might_throw_my_exception();
} catch (const boost::system::system_error& ex) {
  // Also works
}

However, an expression of type my_exception cannot bind to a reference to std::runtime_error (or it's base class, std::exception), due to the ambiguity of the diamond: should it bind to std::system_error's base class object, or should it bind to boost::system::system_error's base class object? The result is that the thrown object is not caught at all:

try {
} catch (const std::runtime_error& ex) {
  // Doesn't catch here!
} catch (...) {
  std::printf("Sadness\n");
}

Godbolt demonstration

The main question: Is there a way to communicate to the compiler which base class subobject shoud be preferred when binding a deadly-diamond-type to a reference-to-base? In normal code you could static_cast<const std::runtime_error&>(ex) or static_cast<const boost::system::system_error&>(ex), but I can't write a static_cast into the compiler's magical exception-handling code.

Secondary question: Is there a better way make my code interoperable with both std:: and boost::system:: error handling?

Aucun commentaire:

Enregistrer un commentaire