Is there any good practice related to dynamic_cast error handling (except not using it when you don't have to)? I'm wondering how should I go about NULL and bad_cast it can throw. Should I check for both? And if I catch bad_cast or detect NULL I probably can't recover anyway... For now, I'm using assert to check if dynamic_cast returned not NULL value. Would you accept this solution on a code review?
It depends... ;-)
If I really expected the dynamic_cast
to give me something usable, for example if I and no one else added a polymorphic type to a container of pointers to a base class, then I would go with the reference cast and let the std::bad_cast
kill my application - there wouldn't be much else to do, really.
However, if I'm querying a polymorphic type for some capability exposed by an interface that it doesn't necessarily have to implement, then I'd go with the pointer cast and then a NULL wouldn't be an error (unless, of course, I expected the capability to really be there - but then I'd had gone for the reference cast in the first place...)
If the dynamic_cast
should succeed, it would be good practice to use boost::polymorphic_downcast
instead, which goes a little something like this:
assert(dynamic_cast<T*>(o) == static_cast<T*>(o));
return static_cast<T*>(o);
This way, you will detect errors in the debug build while at the same time avoiding the runtime overhead in a release build.
If you suspect the cast might fail and you want to detect it, use dynamic_cast
and cast to a reference type. This cast will throw bad_cast
in case of error, and will take down your program. (This is good if, as you say, you are not going to recover anyway)
T& t = dynamic_cast<T&>(o);
t.func(); //< Use t here, no extra check required
Use dynamic_cast
to a pointer type only if the 0-pointer makes sense in the context. You might want to use it in an if
like this:
if (T* t = dynamic_cast<T*>(o)) {
t->func(); //< Use t here, it is valid
}
// consider having an else-clause
With this last option you need to make sure that the execution path makes sense if the dynamic_cast
returns 0.
To answer your question directly: I would prefer one of the two first alternatives I have given to having an explicit assert
in the code :)
bad_cast is only thrown when casting references
dynamic_cast< Derived & >(baseclass)
NULL is returned when casting pointers
dynamic_cast< Derived * >(&baseclass)
So there's never a need to check both.
Assert can be acceptable, but that greatly depends on the context, then again, that's true for pretty much every assert...
I'd concur with the 'it depends' answer, and also add "Graceful degradation": just because a cast fails somewhere isn't enough reason to let the application fail (and the user lose his/her work, etc.). I'd recommend a combination of asserts and defensive programming:
ptr = dynamic_cast<MyClass>(obj);
ASSERT(ptr);
if(ptr)
{
// do stuff
}
Yes and no.
boost::polymorphic_downcast<>
is surely a good option to handle errors of dynamic_cast<>
during the debug phase. However it's worth to mention that polymorphic_downcast<>
should be used only when it's possible to predict the polymorphic type passed at compile time, otherwise the dynamic_cast<>
should be used in place of it.
However a sequence of:
if (T1* t1 = dynamic_cast<T1*>(o))
{ }
if (T2* t2 = dynamic_cast<T2*>(o))
{ }
if (T3* t3 = dynamic_cast<T3*>(o))
{ }
denotes a very bad design that should be settle by polymorphism and virtual functions.