std::nested_exceptions
are nice when all you want to do is calling what()
, but accessing the interface of other exception types gets ugly.
Let us suppose I have two exception classes which store some additional information:
/* CODE BLOCK 1 */
class ErrorI : public std::runtime_error {
public:
ErrorI(int a_integer) : std::runtime_error{"ErrorI"}, integer{a_integer} {}
int integer;
};
class ErrorD : public std::runtime_error {
public:
ErrorD(double a_real) : std::runtime_error{"ErrorD"}, real{a_real} {}
double real;
};
Without nested exceptions, we can access the member variables in the try/catch block:
/* CODE BLOCK 2 */
int main()
{
try {
/* do stuff */;
}
catch(const ErrorI& ee){
std::cout << " Value: " << ee.integer << std::endl;
}
catch(const ErrorD& ee){
std::cout << " Value: " << ee.real << std::endl;
}
}
But if we want to unwrap a std::nested_exception
, things are not so straightforward. We need to define a function which will be called recursively, which should look like this:
/* CODE BLOCK 3 */
void process_exception(const std::exception& e, int level=0) {
try {
std::rethrow_if_nested(e);
}
catch(const std::exception& e) {
process_exception(e, level+1);
}
/* ... process the top-most (latest) exception ... */
}
Unfortunately, for processing the top-most exception, we cannot use the try/catch syntax in Code Block 2: if we rethrow e, it will be truncated to an std::exception, and we will lose all the additional information. EDIT: This is not true if std::rethrow_exception and std::exception_ptr are used.
So we're back to the problem of good-ole dynamic type-checking, with all that it entails (see this for example).
Derive all the exceptions from a common base class with the desired interface. This includes approaches like the Visitor pattern. This is neat, but not good if the exception classes are provided by a external library.
Using dynamic_cast:
/* CODE BLOCK 4 */ if (auto p = dynamic_cast<ErrorI const*>(&e)) { std::cout << " Value: " << p->integer << std::endl; } else if (auto p = dynamic_cast<ErrorD const*>(&e)) { std::cout << " Value: " << p->real << std::endl; }
???
My only option seems to resort to 2. I would love to hear if there is any other suggestion.
With the help of
std::exception_ptr
, you might do something like:Demo