Reputation: 53337
In a library I have a hierarchy of exceptions (RuntimeException -> RecognitionException -> NoViableAltException
). During execution, a NoViableAltException
is thrown, which is caught as below and an exception pointer is created from that:
try {
//code that throws NoViableAltException
} catch (RecognitionException &re) {
std::exception_ptr e = std::make_exception_ptr(re);
reportError(e);
}
The value of e
is used in other places, irrelevant for this question. In reportError()
I actually handle the error:
void reportError(std::exception_ptr e) {
...
try {
std::rethrow_exception(e);
} catch (NoViableAltException &ne) {
reportNoViableAlternative(recognizer, ne);
} catch (InputMismatchException &ne) {
reportInputMismatch(recognizer, ne);
} catch (FailedPredicateException &ne) {
reportFailedPredicate(recognizer, ne);
} catch (RecognitionException &ne) {
recognizer->notifyErrorListeners(ne.getOffendingToken(), ne.what(), e);
}
}
and here's my problem: when I rethrow e
, the NoViableAltException
branch is not taken, but that of the RecognitionException
(the last one). That is surprising and I wonder why this happens. I also tried to catch NoViableAltException*
, to no avail. What is the correct approach to catch the individual exception types?
Upvotes: 10
Views: 2605
Reputation: 477030
Don't use make_exception_ptr
; that's something different (it creates a new exception pointer with a deduced exception type, and your code ends up slicing the caught exception object). Instead, you want to capture the current exception:
catch (RecognitionException &)
{
std::exception_ptr e = std::current_exception();
// ...
}
Upvotes: 14
Reputation:
From documentation for std::make_exception_ptr
:
Creates an std::exception_ptr that holds a reference to a copy of e.
Unfortunately, copying e
means you get object slicing (which @Mohamad Elghawi points out is also more prominently mentioned later on that page). When you call std::make_exception_ptr<RecognitionException>
, it will hold a copy of a RecognitionException
, not any derived class.
But you don't need exception_ptr
at all here. Even though reportError
does not have the try
...catch
in scope, you can still use throw;
to re-throw the current exception.
#include <stdio.h>
struct A { virtual ~A() = default; };
struct B : A { };
void reportError() {
try {
throw;
}
catch (B &) {
puts("caught B");
}
catch (A &) {
puts("caught A");
}
}
int main() {
try {
throw B();
}
catch (A &) {
reportError();
}
}
Upvotes: 9