dynamic_cast is ugly!

D

Daniel T.

James Kanze said:
The basic (abstract) problem that Ian has raised is that of a
heterogeneous collection of objects. It's a frequent problem,
and it's a typical example of a case where dynamic_cast is a
good solution.

The basic problem that he raised is that the type information is being
thrown away when it is still needed.

Now, maybe there really are situations where you have no choice and must
throw away type information. All I can say is that I've never had to
face such a situation.
 
D

Daniel T.

Andy Champ wrote:

So you mean that if dynamic_cast was really, really hard to use, you
would just have had go back and demand a proper redesign of the base
class?

Good point. Also, what if dynamic_cast didn't exist? Note that we have
no language primitive way of knowing who the caller of a function is
(i.e., we can't backtrack along an association arrow,) what if we
couldn't backtrack along an inheritance arrow either? How would that
base class, and all the code that uses it, be designed then?

I doubt that Andy would claim that those programs that use
dynamic_cast simply could not be written without it. After all, the
lack of dynamic_cast would not destroy the turing completeness of the
language.
 
D

Daniel T.

Dynamic_cast has its place in good OO design; it just shouldn't be
abused.

Just like goto? :)

I don't agree that dynamic_cast has its place in *good* OO design, but
it does have its place.
 
J

James Kanze

Just like goto? :)

No. Goto never has its place.
I don't agree that dynamic_cast has its place in *good* OO
design, but it does have its place.

You're obviously using a different definition of OO than I am,
then. Generally speaking, IMHO you should access the object
through defined interfaces; whether they are in the concrete
class, or in an abstract base class, isn't that important in the
absolute, but there will definitely be cases where a concrete
class will want to implement an extended interface. In an ideal
world, in such cases, you wouldn't loose the type, and you'd
always have access to the extended interface. Practically
speaking, however, there are cases where the object will be
passed through generic layers which do loose the type. Being
able to do so is a fundamental part of OO.

Think for a moment about Smalltalk---the granddaddy of OO
languages. Where everything was an Object, and containers only
contained Object. Of course, you didn't need dynamic_cast,
because there was no static typing to begin with. Now, it may
not be OO---in fact, according to most OO proponents, it goes
against OO---but I sort of like static type checking. As long
as I have the flexibility to work around it in cases where the
design forces a loss of the original type.
 
J

Juha Nieminen

Daniel said:
Since I work with such issues on a regular basis, and I never use
dynamic_cast, let me assure you that you can keep track of drawing order
without throwing away type information. How you go about doing it
depends on the design goals.

I'm interested in hearing alternative techniques because I have to do
this as well (as my payjob), and I'm always looking to improve my
library designs.
 
J

James Kanze

It would be fun to sit down with you and see if we can't
connect the code that creates the objects to the code that
uses the objects, then remove those two dynamic_casts.

The question is more: do you want to. Should the code between
the provider and the consumer need to know about all of the
possible interfaces, as long as the provider and the consumer
are in agreement?

And in the end, isn't this the age old argument between static
type checking (a la C++) and dynamic type checking (a la
Smalltalk)? I think most of us here agree that static type
checking provides an additional, very useful measure of
security (and it's worth pointing out that all of the modern OO
languages use static type checking, even though Smalltalk
didn't). On the other hand, that security comes at a loss of
flexibility (true OO). IMHO, dynamic_cast is an almost perfect
way of recovering enough of that flexibility when it's needed,
without any more loss of security than is necessary to achieve
that flexibility---I can't just call any function on an object,
hoping that the object implements it: I must specifically ask
for a different, statically declared interface, in order to
access the additional functions. In other words, in OO in its
"truest" (or at least its original) form, you don't have
dynamic_cast, because the flexibility is built into the
language, because of the absense of static type checking. In
C++ without dynamic_cast, you loose a lot of that flexibility.
In many cases, it doesn't matter, of course, and it's often
preferable to design your code so that it won't. But many cases
isn't all, and there are cases where you need that extra measure
of flexibility.
 
J

James Kanze

The basic problem that he raised is that the type information
is being thrown away when it is still needed.
Now, maybe there really are situations where you have no
choice and must throw away type information. All I can say is
that I've never had to face such a situation.

So you've never used a generic framework?
 
J

John Brawley

in message
On Mar 8, 5:08 am, James Kanze wrote:
No. Goto never has its place.

Oh?
How, then...? :
(Console app.)

for(;;) {
//no exit condition is programmable.
//live, *vital*, user interaction via keyboard.
//check for a keypress (ch).
switch(ch) {
//case
//case
case: 'q' : goto end;
//case: }
end:
return 0;
}
 
D

Daniel T.

James Kanze said:
You're obviously using a different definition of OO than I am,
then. Generally speaking, IMHO you should access the object
through defined interfaces; whether they are in the concrete
class, or in an abstract base class, isn't that important in the
absolute, but there will definitely be cases where a concrete
class will want to implement an extended interface. In an ideal
world, in such cases, you wouldn't loose the type, and you'd
always have access to the extended interface.

It sounds like we aren't that far apart after all. Sometimes
dynamic_cast is necessary, but designing your framework so that one
must use dynamic_cast to use it properly... It doesn't sound like you
would approve of such a design.
Think for a moment about Smalltalk---the granddaddy of OO
languages.

Even in Smalltalk, sending a message to an object without knowing if
the object will understand the message is frowned upon.

I'm not going to pretend that the matter is settled. There is a
regular debate in comp.object over whether designs that require down-
casting are appropriate, but I think it is fair to say that it isn't
*necessary*, and I happen to believe that it *isn't* appropriate.

Which is why, when the Andy asked how often I use it, I said "never,"
and when he asked why not, I said that I don't use it because I design
my code such that its use isn't necessary.
 
D

Daniel T.

So you've never used a generic framework?

Of course I have. My company developed a generic framework 10 years
ago (I helped in designing it) which we still use to this day. It's a
little long in the tooth in some parts, but nobody ever has to
dynamic_cast anything.

Without the use of templates, I can think of situations where
dynamic_cast is necessary, but I would still not design a program such
that I didn't know *at compile time* that the cast would succeed.
 
D

Daniel T.

Daniel said:
Since I work with [heterogeneous containers] on a regular
basis, and I never use dynamic_cast, let me assure you that you
can keep track of drawing order without throwing away type
information. How you go about doing it depends on the design
goals.

I'm interested in hearing alternative techniques because I have to
do this as well (as my payjob), and I'm always looking to improve my
library designs.

This conversation should probably migrate over to comp.object at this
point.

Take the example that Ian brought up. You have a heterogeneous
container that is maintaining object order, but you occasionally seem
to need to pull out objects and query their type... Before you put the
object in the container, you knew its type, then you threw it away
even though you needed it in some latter portion of the program. The
key is to not throw that information away. How you pass that
information from the creator to the user depends on the situation.
 
A

Alf P. Steinbach

* John Brawley:
How, then...? :
(Console app.)

for(;;) {
//no exit condition is programmable.
//live, *vital*, user interaction via keyboard.
//check for a keypress (ch).
switch(ch) {
//case
//case
case: 'q' : goto end;
//case: }
end:
return 0;
}

for( bool finished = false; !finished; )
{
char const ch = keypress();
switch( ch )
{
case 'q':
{
finished = true;
}
}
}

Another possibility is to use "return" from a function.


Cheers, & hth.

- Alf
 
D

d major

How many times have you written

someType* var = dynamic_cast<someType*>(someOtherPtr);

and wondered WTH you have to quote the type twice?

So I built this:

template <class T> class AutoCastProxy
{
public:
  AutoCastProxy(T object): m_Object(object) {};
  template <class U> operator U()
  {
   return dynamic_cast<U>(m_Object);
  };
private:
  T m_Object;

};

template <class T> AutoCastProxy<T> AutoCast(T object)
{
        return AutoCastProxy<T>(object);

};

... which lets you type

someType* var = AutoCast(someOtherPtr);

Surely it's been done before?  If not:

I hereby place this code in the public domain.  I'm not even asking for GPL.

Andy

Anyway, I don't think dynamic_cast ugly, on the contrary, It very
useful when to use in polymiorphism. By dynamic_cast we can realize
RTTI.
 
J

James Kanze

It sounds like we aren't that far apart after all. Sometimes
dynamic_cast is necessary, but designing your framework so
that one must use dynamic_cast to use it properly... It
doesn't sound like you would approve of such a design.

It depends on the context. Making some intermediary aware of
all of the details of what it transporting isn't something I'd
approve of either. (The context was Java Swing, and not C++,
but I've used very generic code for linking up application
specific actions and the buttons in toolbars, etc.)

What is bad, of course, is a long list of:

if ( dynamic_cast< Concrete1* >( p ) ) {
// ...
} else if ( dynamic_cast< Concrete2* >( p ) {
// ...
} else if ( dynamic_cast< Concrete3* >( p ) {
}

I don't find anything particularly bad, however, in the idea
that some concrete types support an extended interface, and that
the ultimate user might do something like:

Extended* px = dynamic_cast< Extended* >( p ) ;
if ( px != NULL ) {
// use the extended interface, e.g. to get more
// information...
} else {
// carry out some default action...
}
Even in Smalltalk, sending a message to an object without
knowing if the object will understand the message is frowned
upon.

Certainly. But in Smalltalk, you could also ask the object if
it understood the method, and do something by default if it
didn't. (But I prefer the C++ solution, where you have to ask
the object if it supports a specific extended interface, and not
just whether it happens to have a method with the targetted
name.)
I'm not going to pretend that the matter is settled. There is
a regular debate in comp.object over whether designs that
require down-casting are appropriate, but I think it is fair
to say that it isn't *necessary*, and I happen to believe that
it *isn't* appropriate.

I would say rather that it is associated with a certain "cost"
(in terms of readability and maintainability), and shouldn't be
used unless the alteratives have a greater cost.
Which is why, when the Andy asked how often I use it, I said
"never," and when he asked why not, I said that I don't use it
because I design my code such that its use isn't necessary.

Well, I don't use it very often, but there are times when the
alteratives involve even greater cost.
 
J

James Kanze

Of course I have. My company developed a generic framework 10 years
ago (I helped in designing it) which we still use to this day. It's a
little long in the tooth in some parts, but nobody ever has to
dynamic_cast anything.

Without the use of templates, I can think of situations where
dynamic_cast is necessary, but I would still not design a program such
that I didn't know *at compile time* that the cast would succeed.
 
J

James Kanze

Of course I have. My company developed a generic framework 10 years
ago (I helped in designing it) which we still use to this day. It's a
little long in the tooth in some parts, but nobody ever has to
dynamic_cast anything.
Without the use of templates, I can think of situations where
dynamic_cast is necessary, but I would still not design a
program such that I didn't know *at compile time* that the
cast would succeed.

Except that templates also have their cost. I'd rather see a
dynamic cast that see a 100,000 LOC framework end up as
templates, all in the header files. And as Juha has pointed
out, using separate queues or lists for each time also looses
order---again, you can organize things to avoid this loss (or to
recover the information later), but is it worth it? Or wouldn't
the extra code be worse than just using dynamic_cast?
 
D

Daniel T.

James Kanze said:
The question is more: do you want to. Should the code between
the provider and the consumer need to know about all of the
possible interfaces, as long as the provider and the consumer
are in agreement?

How do you know that the provider and the consumer are in agreement? Or
to put it another way, the fact that dynamic_cast is in the code shows
that the provider may very well give the consumer something it can't
consume (i.e., they aren't in agreement.)

No, I don't think the transporting code should necessarily know what it
is transporting, but just like in the real world, consumers shouldn't
have to open the box after receiving the item to see if it is really
what they asked for either.
 
J

Juha Nieminen

Daniel said:
The
key is to not throw that information away. How you pass that
information from the creator to the user depends on the situation.

I'm afraid this is just too vague to be of any help to me...
 
G

Greg Herlihy

Oh?
How, then...? :
(Console app.)

for(;;) {
//no exit condition is programmable.
//live, *vital*, user interaction via keyboard.
//check for a keypress (ch).
switch(ch) {
//case
//case
case: 'q' : goto end;
//case: }
end:
return 0;

}

How about:

int main()
{
for(;;)
{
// no exit condition is programmable.
// live, *vital*, user interaction via keyboard.
// check for a keypress (ch).
char ch = keypress();

switch (ch)
{
case 'a':
// do something...
continue;
break;

case 'q':
break;

default:
continue;
break;
}
break;
}
return 0;
}

Greg
 
H

H. S. Lahman

Responding to Nieminen...
I'm afraid this is just too vague to be of any help to me...

I think what Daniel T. is saying is that from a design perspective you
have two choices:

(1) Use separate homogeneous collections rather than heterogeneous
collections. Then the client who needs specific types of objects can
navigate the appropriate relationship to the right collection.

(2) Provide the type information to the collection and provide an
interface to the collection that clients can access by providing a
desired type. Then the collection manages the {type, object} tuples in
its implementation.

Caveat. In both cases the client should understand some problem space
context that happens to map into a type of the collected objects rather
than the object type itself. Then the relationships in (1) can be
instantiated and navigated based on that context while the 'type' in (2)
can be a separate context variable. That allows the client to be
properly decoupled from the object type since the access is defined in
terms of the problem space context rather than OOPL implementation
types. IOW, the mapping of the object type to the problem context that
requires access to a specific type is isolated and encapsulated in
whoever defines the collection content; everyone else deals with the
problem space context.


--
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
(e-mail address removed)
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
(e-mail address removed) for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,167
Latest member
SusanaSwan
Top