468,765 Members | 1,499 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 468,765 developers. It's quick & easy.

Dynamic_cast Vs Static_cast - when is the former really necessary?

Everyone knows about the complex and cpu-expensive procedures taken by
dynamic_cast to find the right function call in a virtual classes
hierarchy.
The question I would to rise is when dynamic_cast is really necessary?
A wise usage of templates and/or non dynamic_cast operators can grant
the compiler with any information it needs to understand at compile
time what is the right method to call.
In the following example I tested the usage of dynamic_cast to call a
sibling method in a simple 4 class hierarchy. I compared it with a
version in wich static_cast is used from the topmost derived class to
pass information to
its super classes. Results were stunnig. Dynamic_cast version perform
at least 14 time slower than the static_cast one.
Let's see the code:

#include <string>
#include <iostream>

extern "C" {

#include <stdlib.h>
#include <time.h>
#include <unistd.h>
}

#define INIT_TIMER long T; time(&T); srand(T);

#define START_TIMER long start0,end0; clock_t start1,end1;
time(&start0); start1 = clock();

#define END_TIMER time(&end0); end1 = clock();

#define PRINT_TIMER cout << "Computation time (sec)" << (end0) -
(start0) << endl; cout << "Computation time (msec)" << (end1) -
(start1) << endl;

#define MAX_VALUE 50000000

using namespace std;

class A;

class BASE {

public:

BASE() { }

virtual int a() = 0;

virtual int b(A* ptr = NULL) = 0;

virtual int c() = 0;

virtual int cS() = 0;

virtual ~BASE() { }
};

class A : virtual public BASE {

public:

A() { }

int a() { return 0; }

virtual ~A() { }
};

class B : virtual public BASE {

public:

B() { }

protected:

int b(A* ptr = NULL) {

if (ptr == NULL) return dynamic_cast<A*>(this)->a() + 1;

else return ptr->a() + 1;
}

virtual ~B() { }
};

class C : virtual public A, virtual public B {

public:

C() { }

int a() { return 2; }

int c() { return b(); }

int cS() { return b(static_cast<A*>(this)); }

~C() { }
};

int main() {

INIT_TIMER

{
cout << "static_cast version " << endl;

BASE* base = new C;

long result = 0;

START_TIMER

for(int i=0; i<MAX_VALUE; i++) {

result += base->cS();
}

END_TIMER

delete base;

cout << "Result " << result << endl;

PRINT_TIMER
}

{
cout << "dynamic_cast version " << endl;

BASE* base = new C;

long result = 0;

START_TIMER

for(int i=0; i<MAX_VALUE; i++) {

result += base->c();
}

END_TIMER

delete base;

cout << "Result " << result << endl;

PRINT_TIMER
}
}

I would like to know comments about that. I also would like to see an
example
in wich dynamic_cast cannot be absolutely removed.
Cheers.

Gianguglielmo

Jul 22 '05 #1
13 4693
GianGuz wrote:
The question I would to rise is when dynamic_cast is really necessary?
When you need to cast a type to another without knowing the validity of
the cast. When using static_cast, you may find yourself with an object
with an invalid type, which will crash your program. dynamic_cast
either returns 0 or throws an exception (for references), alowing you to
detect bad casts.
A wise usage of templates and/or non dynamic_cast operators can
, most of the time,
grant
the compiler with any information it needs to understand at compile
time what is the right method to call.
I agree, with the help of virtual functions.
In the following example I tested the usage of dynamic_cast to call a
sibling method in a simple 4 class hierarchy. I compared it with a
version in wich static_cast is used from the topmost derived class to
pass information to
its super classes. Results were stunnig. Dynamic_cast version perform
at least 14 time slower than the static_cast one.
It is not surprising, since static_cast does (probably) nothing at
run-time, compared with dynamic_cast which has to validate the cast by
checking the class hierarchy.
Let's see the code:
<snip>

Next time, include the results, not the code. Or if you give the code,
indent it correctly and don't use macros.

I would like to know comments about that. I also would like to see an
example
in wich dynamic_cast cannot be absolutely removed.


An example would be the check whether a type can be converted to another
at run-time. That is, by the way, the reason for which dynamic_cast was
introduced.
Jonathan
Jul 22 '05 #2
Jonathan Mcdougall wrote:
GianGuz wrote:
The question I would to rise is when dynamic_cast is really necessary?

When you need to cast a type to another without knowing the validity of the cast. When using static_cast, you may find yourself with an object with an invalid type, which will crash your program. dynamic_cast
either returns 0 or throws an exception (for references), alowing you to detect bad casts.
A wise usage of templates and/or non dynamic_cast operators can
, most of the time,
> grant
the compiler with any information it needs to understand at compile
time what is the right method to call.


I agree, with the help of virtual functions.
In the following example I tested the usage of dynamic_cast to call

a sibling method in a simple 4 class hierarchy. I compared it with a
version in wich static_cast is used from the topmost derived class to pass information to
its super classes. Results were stunnig. Dynamic_cast version perform at least 14 time slower than the static_cast one.


It is not surprising, since static_cast does (probably) nothing at
run-time, compared with dynamic_cast which has to validate the cast

by checking the class hierarchy.
Let's see the code:
<snip>

Next time, include the results, not the code. Or if you give the

code, indent it correctly and don't use macros.

Sorry for the code. I'll do my best next time.
In the meantime here are some results:

CALLING ITERATIONS 50000000

static_cast version
Result 150000000
Computation time (sec)2
Computation time (msec)1530000

dynamic_cast version
Result 150000000
Computation time (sec)24
Computation time (msec)22000000

I would like to know comments about that. I also would like to see an example
in wich dynamic_cast cannot be absolutely removed.


An example would be the check whether a type can be converted to

another at run-time. That is, by the way, the reason for which dynamic_cast was introduced.

The problem here is that : "yes, it is a situation in which you require
dynamic_cast"...but can be avoided? When you design a hierarchy you
have the control on what is compile time determined. Why do you have to
check for type conversion at runtime instead of changing the design to
make it possible at compile time. And this is tipically possible. The
question at this point is: "is always possible to change the design in
such way?"
I would like to see an example that clarify that no other design
choices (without dynamic_cast) are possible to reach the same result.
I hope that my opinion was expressed clearly.
Thanks,

Gianguglielmo
Jonathan


Jul 22 '05 #3
GianGuz wrote:
The problem here is that : "yes, it is a situation in which you require
dynamic_cast"...but can be avoided? When you design a hierarchy you
have the control on what is compile time determined. Why do you have to
check for type conversion at runtime instead of changing the design to
make it possible at compile time. And this is tipically possible. The
question at this point is: "is always possible to change the design in
such way?"
I would like to see an example that clarify that no other design
choices (without dynamic_cast) are possible to reach the same result.


No feature is absolutly necessary in C++ knowing that you could do the
very same thing in assembly. The thing is, not everybody has the chance
to "change the design" of a framework. dynamic_cast may be a worst case
solution, but when you need it, there is nothing in the language which
can emulate it. For example, if you don't have access to the class
definition, you will have no choice but to use dynamic_cast's if you
want the behavior to change wrt the object's type. It is not always
possible to have the cleanest code.
Jonathan
Jul 22 '05 #4
That's it. If you don't have access to class definitions the only way
to make 'inference' about the current hierarchy is to check types at
runtime.
Thanks,

Gianguglielmo

Jul 22 '05 #5
"GianGuz" <gi*****************@noze.it> wrote in message
news:11**********************@f14g2000cwb.googlegr oups.com...
Everyone knows about the complex and cpu-expensive procedures taken by
dynamic_cast to find the right function call in a virtual classes
hierarchy.
The question I would to rise is when dynamic_cast is really necessary?


[snip]

dynamic_cast has a bad reputation, mostly from its (often realized)
potential for abuse. However, it allows us C++ programmers to use multiple
abstract interfaces with our objects just as COM and many other object
models use with theirs. For instance:

class IThing
{
public:
virtual void foo() = 0;
virtual ~IThing() {}
};

class ILog
{
public:
virtual void send_to_log(const std::string &) = 0;
virtual ~ILog() {}
};

void yadda(IThing *p)
{
p->foo(); // I know I can call this
// check to see if an ILog interface was included
ILog *p2 = dynamic_cast<ILog*>(p);
if (p2)
p2->send_to_log("yadda here");
}

I don't do this every day in my own code, but I like having the feature in
the language.

--
Cy
http://home.rochester.rr.com/cyhome/
Jul 22 '05 #6
Your usage of dyamic_cast relies on what we said about the need to
determine a type
information at run-time. In the code you shown I can imagine that you
don't need to
check
....
ILog *p2 = dynamic_cast<ILog*>(p);
if (p2)
....
all the time. So dynamic_cast really does not impact on performance
measures.
What i want to say is that we have to point out that using that cast
operator
to repeatly call a particular method without trying to redesign the
class hierarchy
(when this is possible, of course) in favour of a static_cast -
reinterpret_cast version
is big mistake.
Moreover many compilers (for example gcc) supports flags such as
no-rtti that completely
disables the run time type information system of c++ (dynamic_cast and
type_id)
in favour of code size and execution speed. Another feature to take
into account when
we decide to dynamic_cast something...
Cheers,

Gianguglielmo

Jul 22 '05 #7
GianGuz wrote:
The problem here is that : "yes, it is a situation in which you require
dynamic_cast"...but can be avoided? When you design a hierarchy you
have the control on what is compile time determined. Why do you have to
check for type conversion at runtime instead of changing the design to
make it possible at compile time. And this is tipically possible. The
question at this point is: "is always possible to change the design in
such way?"
I would like to see an example that clarify that no other design
choices (without dynamic_cast) are possible to reach the same result.


That's clearly impossible. You can write any program using any small
turing-complet subset of C++ (such as C).

However, dynamic_cast is most sometimes used for input. e.g.

SomeBase* o = read_from_stream(s);
//what was read? Unknown at compile time
if (Derived1* p = dynamic_cast<Derived1*>(o))
{
//it was a derived1 object
}
else if (etc....

Of course you can use object IDs instead of dynamic_cast, or use, e.g.
the Visitor pattern, but dynamic_cast is a non-intrusive approach.
Speaking of the visitor pattern, dynamic_cast can be used to implement
an acyclic visitor. It is also sometimes used in "multimethod"
implementations.

On a final note, although dynamic_cast is slow on Microsoft compilers
(it does string comparisons due to the potential for dynamically loaded
DLLs), it is faster on most other platforms (which just compare numeric
IDs).

Tom
Jul 22 '05 #8
Tom Widmer wrote:
GianGuz wrote:
The problem here is that : "yes, it is a situation in which you require dynamic_cast"...but can be avoided? When you design a hierarchy you
have the control on what is compile time determined. Why do you have to check for type conversion at runtime instead of changing the design to make it possible at compile time. And this is tipically possible. The question at this point is: "is always possible to change the design in such way?"
I would like to see an example that clarify that no other design
choices (without dynamic_cast) are possible to reach the same
result.
That's clearly impossible. You can write any program using any small
turing-complet subset of C++ (such as C).

Obviously I'm not interest in such low level rewriting.
However, dynamic_cast is most sometimes used for input. e.g.

SomeBase* o = read_from_stream(s);
//what was read? Unknown at compile time
if (Derived1* p = dynamic_cast<Derived1*>(o))
{
//it was a derived1 object
}
else if (etc....

This is the case I need. IMHO we have to clarify WHERE the dynamic_cast
operator HAVE TO BE used and where it HAVE TO BE AVOIDED. Most of the
time simply knowing that it does a lot of work for us seems to be
enough to say:
"ok let's use it" without taking care of alternatives.
I think that having nice features doesn't mean we have to stop
thinking about other, possible better, solutions.
I'm also interested in cases when dynamic_cast checks can be immediatly
substituted by an equivalent use of type_id.
Of course you can use object IDs instead of dynamic_cast, or use, e.g. the Visitor pattern, but dynamic_cast is a non-intrusive approach.
Speaking of the visitor pattern, dynamic_cast can be used to implement an acyclic visitor. It is also sometimes used in "multimethod"
implementations.

On a final note, although dynamic_cast is slow on Microsoft compilers (it does string comparisons due to the potential for dynamically loaded DLLs), it is faster on most other platforms (which just compare numeric IDs).

Tom

I didn't know about that. Well I know about that know!;)

Gianguglielmo

Jul 22 '05 #9
GianGuz wrote:

Please quote the posts you are answering to.
dynamic_cast has a bad reputation, mostly from its (often realized)
potential for abuse. However, it allows us C++ programmers to use multiple
abstract interfaces with our objects just as COM and many other object
models use with theirs. For instance:

class IThing
{
public:
virtual void foo() = 0;
virtual ~IThing() {}
};

class ILog
{
public:
virtual void send_to_log(const std::string &) = 0;
virtual ~ILog() {}
};

void yadda(IThing *p)
{
p->foo(); // I know I can call this
// check to see if an ILog interface was included
ILog *p2 = dynamic_cast<ILog*>(p);
if (p2)
p2->send_to_log("yadda here");
}
Your usage of dyamic_cast relies on what we said about the need to
determine a type
information at run-time.
And what could dynamic_cast do except "determine a type information at
run-time"?
In the code you shown I can imagine that you
don't need to
check
....
ILog *p2 = dynamic_cast<ILog*>(p);
if (p2)
....
all the time. So dynamic_cast really does not impact on performance
measures.
But of course dynamic_cast has an impact on performance! The least you
use it, the better it is, we all agree on that. What's your point?
What i want to say is that we have to point out that using that cast
operator
to repeatly call a particular method without trying to redesign the
class hierarchy
(when this is possible, of course) in favour of a static_cast -
reinterpret_cast version
is big mistake.
That's nonsense. The first thing you learn about casts is to avoid
them. Type-switchs are covered in any good c++ textbook and are never
recommended. What I said in my other post is that you may find yourself
before some code you cannot modify. In this case, writing a wrapper or
something will be better than calling dynamic_cast many times. I did
not advocate repeated usage of dynamic_cast. It's just a "handy
feature", like everything else. Used correctly, it can be a powerful
feature, but used incorrectly, it can clutter the code, make
compilations take hours and be terribly "inefficient (tm)".
Moreover many compilers (for example gcc) supports flags such as
no-rtti that completely
disables the run time type information system of c++ (dynamic_cast and
type_id)
in favour of code size and execution speed. Another feature to take
into account when
we decide to dynamic_cast something...


That's irrelevant and nonsense. Because rtti may be deactivated on some
compilers does not make its usage a "big mistake". Did you know
exceptions, warnings and the scope of for loops can also be deactivated
on most compilers? Does that make them a "big mistake" also?
Jonathan
Jul 22 '05 #10
Yes I know about exceptions,warning and scope of for loops,
but I think that are not so related to our case. The usage
of only two operators: dynamic_cast and type_id force
the program to integrate a support that really could be
avoided in a lot of situations. I considered a big mistake
simply the fact that someone could ignore these problems.
Obviously you are able to choice to use the operator you
prefer since you decide that in your particular application
gains are greater than penalties.

Gianguglielmo

Jul 22 '05 #11
GianGuz wrote:
Yes I know about exceptions,warning and scope of for loops,
but I think that are not so related to our case. The usage
of only two operators: dynamic_cast and type_id force
the program to integrate a support that really could be
avoided in a lot of situations.
If you are saying dynamic_cast can/is misused, then I agree.
I considered a big mistake
simply the fact that someone could ignore these problems.
Obviously you are able to choice to use the operator you
prefer since you decide that in your particular application
gains are greater than penalties.


That's a programmer's job.
Jonathan
Jul 22 '05 #12
GianGuz wrote:
Yes I know about exceptions,warning and scope of for loops,
but I think that are not so related to our case. The usage
of only two operators: dynamic_cast and type_id force
the program to integrate a support that really could be
avoided in a lot of situations.
Some implementations of exception handling use the same infrastructure
to do the matching of throws with catch clauses. So the space isn't
wasted in that case.

In any case, we're not talking about a lot of overhead, at least not in
the usual single inheritence case.

I considered a big mistake simply the fact that someone could ignore these problems.
The overhead of RTTI if you don't use it is just a couple of extra
entries in vtables, so a small code side overhead. A clever compiler
could remove the overhead for all hierarchies that don't have
dynamic_cast/typeid used on them.
Obviously you are able to choice to use the operator you
prefer since you decide that in your particular application
gains are greater than penalties.


If you are basically inventing your own RTTI system, then that is
definitely worse than using the one available, unless the built in RTTI
has proved too slow in profiling.

Tom
Jul 22 '05 #13
"GianGuz" <gi*****************@noze.it> wrote in
news:11*********************@f14g2000cwb.googlegro ups.com:
Yes I know about exceptions,warning and scope of for loops,
but I think that are not so related to our case. The usage
of only two operators: dynamic_cast and type_id force
the program to integrate a support that really could be
avoided in a lot of situations. I considered a big mistake
simply the fact that someone could ignore these problems.


I believe that pros and contras were considered carefully *before* adding
these features to the language, and I'm quite sure no ignorance can be
contributed to Bjarne Stroustrup.

In C++ you can shoot your leg in any moment. Dynamic_cast is no
different. If a programmer uses it in a tight time-critical loop then
this programmer is ignorant indeed.

C++ follows the zero-overhead policy, i.e. both features you mention are
such that if they are not used they do not influence significantly the
program performance. Yes they have some impact, for example in the realm
of shared libraries dynamic_cast is finally resorting to string
comparisons, which means the executable code must have the class names
stored somewhere. Ditto for typeid. Hence the compiled program is larger
than it should be. On the other hand I'm sure that if the compiler and
linker can prove that the program does not use RTTI and does not load
shared libraries, they could strip this code from the final executable
(don't know or care if any do that).

The executable size usually impacts only embedded processors or otherwise
memory-critical applications. In a common desktop system the unused parts
of the code never get paged in, so the loss is marginal. Only in order to
support memory-critical applications most compilers have options to
switch RTTI off.

Paavo

Jul 22 '05 #14

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

3 posts views Thread by alg | last post: by
11 posts views Thread by Jamie Burns | last post: by
8 posts views Thread by Thomas Lorenz | last post: by
15 posts views Thread by Grizlyk | last post: by
15 posts views Thread by Bo Yang | last post: by
25 posts views Thread by lovecreatesbea... | last post: by
1 post views Thread by CARIGAR | last post: by
reply views Thread by Marin | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.