Static cast vs. dymamic cast for traversing inheritance hierarchies - c++

I saw one book on C++ mentioning that navigating inheritance hierarchies using static cast is more efficient than using dynamic cast.
Example:
#include <iostream>
#include <typeinfo>
using namespace std;
class Shape { public: virtual ~Shape() {}; };
class Circle : public Shape {};
class Square : public Shape {};
class Other {};
int main() {
Circle c;
Shape* s = &c; // Upcast: normal and OK
// More explicit but unnecessary:
s = static_cast<Shape*>(&c);
// (Since upcasting is such a safe and common
// operation, the cast becomes cluttering)
Circle* cp = 0;
Square* sp = 0;
// Static Navigation of class hierarchies
// requires extra type information:
if(typeid(s) == typeid(cp)) // C++ RTTI
cp = static_cast<Circle*>(s);
if(typeid(s) == typeid(sp))
sp = static_cast<Square*>(s);
if(cp != 0)
cout << "It's a circle!" << endl;
if(sp != 0)
cout << "It's a square!" << endl;
// Static navigation is ONLY an efficiency hack;
// dynamic_cast is always safer. However:
// Other* op = static_cast<Other*>(s);
// Conveniently gives an error message, while
Other* op2 = (Other*)s;
// does not
} ///:~
However, both dynamic cast and static cast (as implemented above) need RTTI enabled for such navigation to work. It's just that dynamic cast requires the class hierarchy to be polymorphic (i.e. base class having at least one virtual function).
Where does this efficiency gain for static cast come from?
The book does mention that dynamic cast is the preferred way for doing type-safe downcasting.

static_cast per se DOESN'T need RTTI -- typeid does (as does dynamic_cast), but that's a completely different issue. Most casts are just telling the compiler "trust me, I know what I'm doing" -- dynamic_cast is the exception, it asks the compiler to check at runtime and possibly fail. That's the big performance difference right there!

It's much better to avoid switching on types at all if possible. This is usually done by moving the relevant code to a virtual method that is implemented differently for different subtypes:
class Shape {
public:
virtual ~Shape() {};
virtual void announce() = 0; // And likewise redeclare in Circle and Square.
};
void Circle::announce() {
cout << "It's a circle!" << endl;
}
void Square::announce() {
cout << "It's a square!" << endl;
}
// Later...
s->announce();
If you are working with a pre-existing inheritance hierarchy that you can't change, investigate the Visitor pattern for a more extensible alternative to type-switching.
More info: static_cast does not require RTTI, but a downcast using it can be unsafe, leading to undefined behaviour (e.g. crashing). dynamic_cast is safe but slow, because it checks (and therefore requires) RTTI info. The old C-style cast is even more unsafe than static_cast because it will quietly cast across completely unrelated types, where static_cast would object with a compile-time error.

With the static cast (and typeid check) you cannot downcast to an intermediate type (child derives from father derives from grandfather, you cannot downcast from grandfather to father) the usage is a little more limited. static_cast without the typeid check is sacrificing correctness for perfomance, and then you know what they say:
He who sacrifices correctness for performance deserves neither
Then of course, there are situations where you are in desperate need of a few CPU instructions and there is nowhere else to look for improvements and you are actually safe on what you are doing and you have meassured (right?) that the only place to gain performance is using static_cast instead of dynamic_cast... then you know you must rework your design, or your algorithms or get better hardware.
The restrictions you impose by using rtti + static_cast is that you will not be able to extend your code with new derived classes at a later time without reworking all places where you have used this trick to gain just a few CPU instructions. That reworking itself will probably take more time (engineering time that is more expensive) than the CPU time you have obtained. If, at any rate, the time devoted to downcasts is noticeable, then rework your design as j_random_hacker suggests, it will improve both in design and performance.

dynamic_cast would return NULL if you hadn't done the typeid check and the cast couldn't succeed. static_cast would succeed (and lead to undefined behavior, such as an eventual crash). That's likely the speed difference.

Related

Dynamic_cast compatibility?

I was reading about dynamic_cast and then I encountered the following statement (from cplusplus.com):
Compatibility note: This type of dynamic_cast requires Run-Time Type
Information (RTTI) to keep track of dynamic types. Some compilers
support this feature as an option which is disabled by default. This
needs to be enabled for runtime type checking using dynamic_cast to
work properly with these types.
After the example:
// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;
class Base { virtual void dummy() {} };
class Derived: public Base { int a; };
int main () {
try {
Base * pba = new Derived;
Base * pbb = new Base;
Derived * pd;
pd = dynamic_cast<Derived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast.\n";
pd = dynamic_cast<Derived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast.\n";
} catch (exception& e) {cout << "Exception: " << e.what();}
return 0;
}
What does the author mean by "this type of dynamic_cast"? Isn't dynamic_cast only used for polymorphic classes(almost)? And he mentions this RTTI as something that is needed for the dynamic cast to work, does that mean that you have to use dynamic_cast with caution because you do not know if it is supported fully by the compiler and therefore makes it riskier than the other casting operators which do not need this RTTI?
The compatibility note relates to the immediately preceding paragraph (and code example):
But dynamic_cast can also downcast (convert from pointer-to-base to pointer-to-derived) polymorphic classes (those with virtual members) if -and only if- the pointed object is a valid complete object of the target type.
And it's true: downcasting requires an object of polymorphic type, and RTTI to traverse the object's inheritance tree at runtime.
The other type of dynamic_cast is explained in the paragraph before that:
This naturally includes pointer upcast (converting from pointer-to-derived to pointer-to-base), in the same way as allowed as an implicit conversion.
No RTTI is required here as an object's base(s) is/are always known statically.
So you only need to fully read the surrounding text in order to understand the context of the words you're reading.
I would note, however, that in my experience a compiler with RTTI disabled by default is basically unheard of. I'm not saying that none exist — there may be some niche, industry-specific compilers targeting embedded platforms that do this to save the programmer a few bytes in their Makefile. But the compilers that most people use (GCC, Clang, Visual Studio, ICC, Comeau) all, to the best of my knowledge, pack RTTI as standard and leave it on until you ask for it to be turned off.
The author in the section mentioned by you referred to the cases when you are using dynamic_cast with polymorphic types: to be a little bit more precise, when you write something like dynamic_cast<X*>(p).
In cases like that, you are going to need Run-Time Type Information so that dynamic_cast can be used at all (see the example below).
You can make the compiler disable the generation of such information about every class with virtual functions by using the mentioned compiler option, -fno-rtti, but it's rarely recommended.
"Other" cases are about the usage of dynamic_cast for void*s.
For example, consider the following code:
class A {
public:
virtual ~A() = default;
};
class B : public A {};
int main()
{
A *p = new B();
void *pv = dynamic_cast<void*>(p);
//B *pb = dynamic_cast<B*>(p);
delete p;
return 0;
}
If you compile the code with g++ test.cpp -std=c++11 -fno-rtti, it's gonna be just fine. But, if you do the same after uncommenting B *pb = dynamic_cast<B*>(p);, the compiler is going to give the following error message for this specific line: error: ‘dynamic_cast’ not permitted with -fno-rtti. Note that the cast to void* works even if using -fno-rtti (which has been set either manually or by default).
rtti is expensive at runtime and some embedded systems compile with flags, disabling it. All it gives you are dynamic_cast and typeid.
Therefore, I interpret
because you do not know if it is supported fully by the compiler
as
because your code could have been compiled with rtti disabled.

Are virtual functions the only way to achieve Runtime Polymorphism in C++?

One of my friends asked me "How Runtime Polymorphism is achieved in C++?" I answered "By Inheritance"
He said "No, it can be achieved only using virtual functions".
So I gave him an example of the following code :-
#include<iostream>
using namespace std;
class A
{
public:
int i;
A(){i=100;}
};
class B : public A
{
public:
int j;
B(){i = -1; j = 99;}
};
void func(A& myA)
{
cout<<myA.i << endl;
}
int main()
{
B b;
A* a = new B();
func(*a);
func(b);
delete a;
return 0;
}
Here, function func() takes reference of A but we pass object of B and we can print the value of public member "i". He said it is compile time polymorphism.
My questions are :-
1) Is Runtime polymorphism achieved only with virtual functions?
2) Is the example above has runtime polymorphism or compile time?
3) If I have the following code :-
void func2(A& myA)
{
cout << myA.i << endl;
// dynamic/static cast myA to myB
cout<<myB.j << endl;
}
what kind of polymorphism is it? Or is it even polymorphism?
The example does not show dynamic polymorphism. The method to be called is known at compile time. There is no runtime decision(based on actual object type) as to which method should be called. There is no different behavior for different types.
For the example to be example of dynamic polymorphism.
You need to provide a virtual member function in Base class and overide it in derived class. The actual method to be called is decided by the actual type of the object pointed by the Base class pointer.
Online sample:
#include<iostream>
using namespace std;
class A
{
public:
virtual void doSomething()
{
std::cout<<"\nIn A::doSomething()";
}
};
class B : public A
{
public:
virtual void doSomething()
{
std::cout<<"\nIn B::doSomething()";
}
};
int main()
{
B b;
A obj;
A* a = &b;
a->doSomething();
a = &obj;
a->doSomething();
return 0;
}
Output:
In B::doSomething()
In A::doSomething()
Is Runtime polymorphism acheived only with virutal functions?
No, but virtual functions is the most common and correct way to do so.
Polymorphism can be achieved through function pointers. Consider the following code example, the actual method to call is decided at run-time depending on user input. It is a form of polymorphism through not in strict sense C++ sense which mandates different behaviors for different types.
#include <iostream>
typedef void (*someFunction)(int, char*);
void FirstsomeFunction(int i, char *c)
{
std::cout<<"\n In FirstsomeFunction";
}
void SecondsomeFunction(int i, char *c)
{
std::cout<<"\n In SecondsomeFunction";
}
int main()
{
someFunction arr[1];
int x = 0;
std::cin >> x;
if(x ==0)
arr[0] = &FirstsomeFunction;
else
arr[0] = &SecondsomeFunction;
(arr[0])(10,"Hello");
return 0;
}
Is the example above has runtime polymorphism or compile time?
There is no polymorphism of any kind. The same method will be called in all cases. There is no different behavior for different types and hence it does not classify as polymorphism of any kind.
The C language's fprintf is a polymorphic function.
You can pass it various handles and it can print to a file, stdout, a printer, a socket, anything which the system can represent as a stream.
FILE* file = fopen("output.txt", "w"); // a file
FILE* file = stdout; // standard output
FILE* file = fopen("/dev/usb/lp0", "w"); // a usb printer
FILE* file = popen("/usr/bin/lpr -P PDF", "w"); // a PDF file
FILE* file = fdopen(socket(AF_INET,SOCK_STREAM,0), "r+"); // network socket
fprintf(file, "Hello World.\n");
what you wrote is not polymorphism.
This is how you do polymorphism in C++ :
#include<iostream>
using namespace std;
class A
{
public:
virtual void func(){
cout << "printing A" << endl;
}
virtual ~A(){}
};
class B : public A
{
public:
void func(){
cout << "printing B" << endl;
}
};
int main()
{
A* a = new A();
A* b = new B();
a->func(); // "printing A"
b->func(); // "printing B"
delete a;
delete b;
return 0;
}
If you were to remove the virtual keyword, the method func of A would be called twice.
One of my friends asked me "How Runtime Polymorphism is achieved in C++?" I answered "By Inheritance"
He said "No, it can be achieved only using virtual functions".
First, the term polymorphism is ambiguous: in the general computing science sense it refers to an ability to implicitly invoke type-specific code, whether at compile time or run-time. In the C++ Standard it is defined very narrowly are being virtual dispatch (that's the perogative of standards). Obviously for your friend's question to be meaningful, as he's asking how it's achieved in C++ his perspective must be from outside C++ - in the larger context of Computing Science terminology.
Certainly, virtual functions/dispatch are an answer, but are they the only answer...?
To attempt to answer that, it helps to have a clear conception of what behaviour qualifies as run-time polymorphic. Consider:
void f(X& x)
{
// the only situation where C++ doesn't know the run-time type of a variable is
// where it's an instance of a class/struct accepted by reference or pointer
...some operation involving "x"...
}
Any mechanism that could result in different machine code for the operation being invoked involving "x", where the reason relates specifically to the run-time type of "x", is getting pretty close to run-time polymorphic, but there's one final issue: was that branching decided implicitly by the language, or arranged explicitly by the programmer?
In the case of virtual dispatch, the compiler implicitly knows to create the virtual dispatch tables and lookups that branch to the type-appropriate code.
But, say we have a function pointer that was previously set to address type-appropriate code, or a type-specific number or enum that is used to control a switch to a type-specific case. These functionally achieve the same behaviour as run-time virtual dispatch, but the set up had to be done explicitly by the developer, and there's no compiler enforcement to make sure that the determination is done purely on run-time type. Whether they qualify or not is arguable. Because C++ has a fully implicit mechanism in virtual dispatch, and because in the C++ Standard polymorphism has a narrowed definition related specifically to virtual dispatch, I'd guess that most C++ programmers would say "no".
But in the world of C, describing say qsort or bsearch (two Standard libC functions that handle arbitrary types using run-time dispatch via function pointer arguments) as run-time polymorphic might aid quick understanding... it's more normal to say that they're generic implementations though.
Still, there's doubtless hundreds of Computing Science textbooks out there with functional definitions of run-time polymorphism, and I'd bet dispatch using function pointers or other programmer-initialised metadata satisfied a good percentage of them. So, it's pointless to be too insistent that there's a single unambiguous answer.
My questions are :-
1) Is Runtime polymorphism achieved only with virtual functions?
As above, I'd lean towards "yes" in the context of C++, but it's (endlessly) arguable.
2) Is the example above has runtime polymorphism or compile time?
Neither... there's not even two functions to choose between on the basis of type - you're always running the same machine code for func(): that picked by the compiler given an expectation that the type is A.
3) If I have the following code :-
void func2(A& myA)
{
cout << myA.i << endl;
// dynamic/static cast myA to myB
cout<<myB.j << endl;
}
what kind of polymorphism is it? Or is it even polymorphism?
Not polymorphic at all, as you have no branching based on type. A dynamic cast could consult the compiler-populated type meta-data in the run-time type of myA, and if you used that to only conditionally invoke the access to myB.j - which would be undefined behaviour unless myA was a B - then you're back at manually, explicitly developer coordinated type-specific behaviour, and whether that qualifies as "polymorphism" for you is discussed above.
Polymorphism is achieved with virtual functions. But to have any effect, i.e. different behaviour depending on type, you need inheritance too
struct A {
virtual void f() = 0;
};
struct B : public A {
void f() {
// do B things
std::cout << "B::f() called\n";
}
};
struct C : public A {
void f() {
// do C things
std::cout << "C::f() called\n";
}
};
Now you can have pointers or references to A with different behaviour, depending on whether it's a B or C.
[C++]
Polymorphism is defined as one interface to control access to a general class of actions. There are two types of polymorphism one is compile time polymorphism and the other is run time polymorphism. Compile time polymorphism is functions and operators overloading. Runtime time polymorphism is done using inheritance and virtual functions.
Polymorphism means that functions assume different forms at different times. In case of compile time it is called function overloading. For example, a program can consist of two functions where one can perform integer addition and other can perform addition of floating point numbers but the name of the functions can be same such as add. The function add() is said to be overloaded. Two or more functions can have same name but their parameter list should be different either in terms of parameters or their data types. The functions which differ only in their return types cannot be overloaded. The compiler will select the right function depending on the type of parameters passed. In cases of classes constructors could be overloaded as there can be both initialized and uninitialized objects.

C++ struct reinterpret_cast

Suppose there are two struct A and B. They have a common struct C.
I would like to know if it is safe to call reinterpret_cast to A or B to C.
if not, is there any way to do so without any performance impact?
struct C
{
string m_c1;
int32_t m_c2;
double m_c3;
string m_c4;
};
struct A
{
C m_a1;
string m_a2;
int32_t m_a3;
};
struct B
{
C m_b1;
string m_b2;
int32_t m_b3;
double m_b4;
};
int main(int argc,char *argv[])
{
A a;
a.m_a1.m_c1="A";
a.m_a1.m_c4="AA";
B b;
b.m_b1.m_c1="B";
b.m_b1.m_c4="BB";
C* pc = reinterpret_cast<C*>(&a);
cout << pc->m_c1 << " " << pc->m_c4 << endl;
pc = reinterpret_cast<C*>(&b);
cout << pc->m_c1 << " " << pc->m_c4 << endl;
return 1;
}
As Mike DeSimone points out the string class is not guaranteed to be a standard-layout class, and thus the class C is not standard-layout, which means that you have no guarantees of the memory layout at all. So it is not safe. Only if you change the string to a (const) char* it will be guaranteed to be safe.
Even then it will only be safe as long as the layout of the classes stays the same (you cannot change the order of members or change the access specifier on them), and the classes stays without any vtables, this is "safe" in such a way that the compiler will generate code that display the behavior you would like.
This is how ever two guarantees that a software developer seldom is able to give. Also the code is hard to understand written like this. Another developer (or the same developer a month later) might ignore the this code (or simply don't understand it), and do the changes needed, and suddenly the code is broken and you got some hard to catch errors on your hand.
A and B are classes that give access to a C (or some members of C). More readable and thus more safe solutions are:
Create an accessor for both A and B, this would probably be inlined and incur no performance penalty.
If there is any reason for inheritance use simple inheritance. Either A and B is-a ClassThatHasAC or A and B is-a C As long as there are no virtual functions you would probably not see any performance issues here either. In both cases an accessor would provide you benefits probably without any performance cost.
Create some simple and readable code at first, and measure performance. Onli if this C access is costing you too much, optimize. But if your optimization boils down to the reinterpret cast trick make sure that there are plenty of warning signs around to make sure that no one steps on this booby trap.
Why dont you inherit both A,B from C and then use static_cast instead? Should be safer/cleaner.
Indeed in your case, you shouldnt need a cast at all, you should be able to assign A or B ptrs to a C*

Need clarifications in C-style, reinterpret, and const casts

Am I right in assuming that C-style casts (which are discouraged) are nothing but reinterpret_casts? Using the latter is visually striking and easy to search when looking for nasty casts, and hence it's recommended over C-style casts?
If casting away const using const_cast and writing to a originally const object is undefined, what is the purpose of const_cast?
Note: I know that Bjarne rightly condemns casting operations that they are unsafe and even goes to the extent of stating "An ugly operation should have an ugly syntactic form." and hence the verbosity of casting operators in C++. So I'll try to minimize their usage. Promise. :)
No. A C cast can do the equivalent of a const_cast, a static_cast, a reinterpret_cast, or a combination thereof. In case that wasn't quite enough, it can also do at least one minor trick that no combination of the newer casts can do at all!
You can use const_cast with defined results if the original variable is defined without const, but all you have is a const pointer or reference to that object. OTOH, if you think you have a good reason to use a const_cast, chances are that you should really look up mutable instead.
Edit: I suppose I should have said it right off, but a C-style cast can convert to an an inaccessible base class. For example, consider something like:
[Edit: I'm updating the code to something that'll compile and (usually) demonstrate problem. ]
#include <iostream>
class base1 {
public:
virtual void print() { std::cout << "base 1\n"; }
};
class base2 {
public:
virtual void print() { std::cout << "base 2\n"; }
};
class derived : base1, base2 {}; // note: private inheritance
int main() {
derived *d = new derived;
base1 *b1 = (base1 *)d; // allowed
b1->print(); // prints "base 1"
base2 *b2 = (base2 *)d; // also allowed
b2->print(); // prints "base 2"
// base1 *bb1 = static_cast<base *>(d); // not allowed: base is inaccessible
// Using `reinterpret_cast` allows the code to compile.
// Unfortunately the result is different, and normally won't work.
base1 *bb2 = reinterpret_cast<base1 *>(d);
bb2->print(); // may cause nasal demons.
base2 *bb3 = reinterpret_cast<base2 *>(d);
bb3->print(); // likewise
return 0;
}
The code using the reinterpret_casts will compile -- but attempting to use the result (of at lest one of the two) will cause a major problem. The reinterpret_cast takes the base address of the derived object and attempts to treat it as if it was the specified type of base object -- and since (at most) one base object can actually exist at that address, trying to treat it as the other can/will cause major problems. Edit: In this case, the classes are essentially identical except for what they print, so although anything could happen, with most compilers, both of the last two will print out "base 1". The reinterpret_cast takes whatever happens to be at that address and tries to use it as the specified type. In this case, I've (tried to) make that do something harmless but visible. In real code, the result probably won't be so pretty.
The C-style cast will work like a static_cast would if the code had used public inheritance instead of private -- i.e. it's aware of where in the derived class each base class object "lives", and adjusts the result, so each resulting pointer will work because it's been adjusted to point at the right place.
No, C-style casts can act as reinterpret_casts, const-casts or static_casts depending on the situation. This is why they are discouraged - you see a C-style cast in code and need to look for details to see what it will do. For example:
const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const
Remember, that a const cast may be acting on something other then the original identifier:
void doit(const std::string &cs)
{
std::string &ms = const_cast<std::string &>(cs);
}
int main()
{
std::string s;
doit(s);
}
So while doit is casting away const, in this example the underlying string is not const so no undefined behavior.
Update
Okay, here's a better example of when using const_cast is not completely worthless. We start with a base class with a virtual function that takes a const parameter:
class base
{
public:
virtual void doit(const std::string &str);
};
and now you want to override that virtual function.
class child : public base
{
public:
virtual void doit(const std::string &str)
{
std::string &mstr = const_cast<std::string &>(str);
}
};
Because of the logic/structure of your code, you know that child::doit will only be called with non-const strings (and class base is not under your control so you can't modify it nor can you change the signature of child::doit because then it will not longer override base::doit). In this case, it's safe to cast away const.
Yes, this is risky. Perhaps when you write that, it's true that the execution will never reach child::doit with a non-const string and the code is valid. But that could change either while maintaining your program or perhaps when you rebuild and pick up the latest version of class base.
const_cast is used to remove const from a type. It also can remove volatile. If the object really is const then the result cannot be written to and still be well-defined behavior. If, however, it is promoted to const (by being passed into a const T function, then const_casting it back to non-const is ok. ( i found some more info here)
reinterpret_cast cannot remove const or volatile from a type.
see also
C-style casts are really the sledge hammer of programming - you basically tell the compiler that the square peg over there will fit through this round hole no matter what. In that sense, reinterpret_cast is very similar.
The main advantage I see in using the C++-style cast operators are that they allow you to express your intent better and allow the compiler to still to some checking on the operation you're asking it to perform rather than the one-size-fits-all style C cast.
Regarding const_cast- you often get into the situation where you are passing an object around via const reference simply because the API requires you to do this. Say, you've got function X that tasks a C-style string:
void X(const char *str) { ... }
Inside that function you're passing the parameter to a C function that expects a char *, even though it's not changing the string. The only way to accommodate this would be to const_cast str.
I'd be very careful using any sort of cast, often this shows that there is something not quite right with your design but sometimes you have to convince the compiler that the peg it's looking at isn't as square as it assumes. Only then should you use the cast operators.

Is there a faster way to detect object type at runtime than using dynamic_cast?

I have a hierarchy of types - GenericClass and a number of derived classes, InterestingDerivedClass included, GenericClass is polymorphic. There's an interface
interface ICallback {
virtual void DoStuff( GenericClass* ) = 0;
};
which I need to implement. Then I want to detect the case when GenericClass* pointer passed into ICallback::DoStuff() is really a pointer to InterestingDerivedClass:
class CallbackImpl : public ICallback {
void DoStuff( GenericClass* param ) {
if( dynamic_cast<InterestingDerivedClass*>( param ) != 0 ) {
return; //nothing to do here
}
//do generic stuff
}
}
The GenericClass and the derived classes are out of my control, I only control the CallbackImpl.
I timed the dynamic_cast statement - it takes about 1400 cycles which is acceptable for the moment, but looks like not very fast. I tried to read the disassembly of what is executed during dynamic_cast in the debugger and saw it takes a lot of instructions.
Since I really don't need the pointer to the derived class is there a faster way of detecting object type at runtime using RTTI only? Maybe some implementation-specific method that only checks the "is a" relationship but doesn't retrieve the pointer?
I always look on the use of dynamic_cast as a code smell. You can replace it in all circumstances with polymorphic behaviour and improve your code quality. In your example I would do something like this:
class GenericClass
{
virtual void DoStuff()
{
// do interesting stuff here
}
};
class InterestingDerivedClass : public GenericClass
{
void DoStuff()
{
// do nothing
}
};
class CallbackImpl : public ICallback {
void DoStuff( GenericClass* param ) {
param->DoStuff();
}
}
In your case, you cannot modify the target classes, you are programming to a contract implied by the declaration of the GenericClass type. Therefore, there is unlikely to be anything that you can do that would be faster than dynamic_cast would be, since anything else would require modifying the client code.
As others have said, using a virtual function is good practice. There is another reason for using it, not applicable in your case, as you can't add the VF, but still worth mentioning I think - it may be much faster than using dynamic cast. In some (not very stringent) tests I've done with g++, the virtual function out-performed the dynamic_cast by a factor of 4.
Because there is such a disparity, it may be worth creating your own inheritance hierarchy which wraps the code you don't control. You would create instances of the wrapper using dynamic_cast (once) to decide what derived type to create, and then use virtual functions from there on.
Looks like a pretty hackish design to me. (As others have mentioned, having to use dynamic_cast is usually a sign that you have a design problem.). But if most of the code is out of your control, there's not much you can do about it, I suppose.
But no, the only general solution I'm aware of is dynamic_Cast. typeid only matches the most derived type, which may work in your case.
On a side note, the reason dynamic_cast is so expensive may be that it has to traverse the entire class hierarchy. If you have a deep hierarchy, that becomes expensive (in other words, don't have a deep hierarchy, in general, but especially in C++).
(Of course the first time you perform the cast, most class descriptor lookups will probably be cache misses, which may have skewed your benchmarking and made it look more expensive than it is)
Would comparing type_infos be any faster? (call typeid on parameter param)
First off, do not optimize prematurely. Second, if you do query an object for a concrete implementation inside, it's likely that there's something wrong with your design (think double dispatch).
As for the original question, introducing a GetRuntimeType() function to a ICallback will do quite nicely: see MFC for on how this can be done.
In your concrete use case, the answer is to use virtual functions.
There are situations, however, where you need to downcast dynamically. There are a few techniques to make this operation faster (or much faster depending on how smart your compiler implements dynamic_cast), in particular, if you limit yourself to single inheritance. The main idea is that if you somehow know the exact type, a static_cast is much faster:
f(A* pA)
{
if (isInstanceOfB(pA))
{
B* pB = static_cast<B*>(pA);
// do B stuff...
}
}
Of course, the problem is now giving a fast implementation of isInstanceOfB().
See boost::type_traits, for instance.
Standard dynamic_cast is very flexible, but usually very slow, as it handles many corners cases you are probably not interested about. If you use single inheritances, you can replace it with a simple implementation based on virtual functions.
Example implementation:
// fast dynamic cast
//! Fast dynamic cast declaration
/*!
Place USE_CASTING to class that should be recnognized by dynamic casting.
Do not forget do use DEFINE_CASTING near class definition.
*\note Function dyn_cast is fast and robust when used correctly.
Each class that should be used as target for dyn_cast
must use USE_CASTING and DEFINE_CASTING macros.\n
Forgetting to do so may lead to incorrect program execution,
because class may be sharing _classId with its parent and IsClassId
will return true for both parent and derived class, making impossible'
to distinguish between them.
*/
#define USE_CASTING(baseType) \
public: \
static int _classId; \
virtual size_t dyn_sizeof() const {return sizeof(*this);} \
bool IsClassId( const int *t ) const \
{ \
if( &_classId==t ) return true; \
return baseType::IsClassId(t); \
}
//! Fast dynamic cast root declaration
/*!
Place USE_CASTING_ROOT to class that should act as
root of dynamic casting hierarchy
*/
#define USE_CASTING_ROOT \
public: \
static int _classId; \
virtual size_t dyn_sizeof() const {return sizeof(*this);} \
virtual bool IsClassId( const int *t ) const { return ( &_classId==t ); }
//! Fast dynamic cast definition
#define DEFINE_CASTING(Type) \
int Type::_classId;
template <class To,class From>
To *dyn_cast( From *from )
{
if( !from ) return NULL;
if( from->IsClassId(&To::_classId) )
{
assert(dynamic_cast<To *>(from));
return static_cast<To *>(from);
}
return NULL;
}
That said, I complete agree with others dynamic_cast is suspicious and you will most often be able to achieve the same goal in a lot cleaner way. That said, similiar to goto, there may be some cases where it can be really useful and more readable.
Another note: if you say the classes in question are out of your control, this solution will not help you, as it requires you to modify the classes (not much, just just add a few lines, but you need to modify them). If this is really the case, you need to use what the language offers, that is dynamic_cast and typeinfo.
Can you use http://www.boost.org/doc/libs/1_39_0/boost/type_traits/is_convertible.hpp
and check the CPU Performance ?
You can also checkout the implementation..
Reference: http://www.boost.org/doc/libs/1_39_0/libs/type_traits/doc/html/boost_typetraits/reference/is_convertible.html