dynamic_cast is ugly!

I

Ian Collins

Daniel said:
There are occasions when the interface forces your hand. For example
when implementing the W3C Document Object Model, where all of the
container types are collections of the the base object (Node). Node is
seldom used, most containers end up storing derived objects (Elements or
Attributes) that extend the functionality of Node.

Say you have an element type with an attribute you want to use (the link
in an XHTML anchor element for instance) and you wish to process all of
these elements in a document. The DOM interface provides a means of
extracting a list of them by name, but that list is a list of Nodes and
Node doesn't even have attributes!

I prefer to be able to write something like

dom::NodeList anchors(document.getElementsByTagName("A"));

html::Anchor a(anchors[n]);

and let the library do the conversion from Node to Anchor under the
hood. One benefit of using dynamic_cast is the conversion will fail if
the Node isn't the expected type.

I will be happy to grant that if you are coding in a representational
style instead of Object Oriented, you may very well have to use
dynamic_cast. I don't code that way, nor do any of the libraries I
use.

OK, given an interface with the restrictions I mentioned above, how
would you code it in an "OO" style?
 
A

Andy Champ

Daniel said:
You didn't provide a requirements document so I can't give any hint of
what the hundereds of other design possibilities might be.

Instead you presented one spicific design that had a spicific problem,
and showed how dynamic_cast can fix the problem. I'm simply saying
that if you don't design your code like that in the first place, you
won't have the problem presented, and therefore won't need
dynamic_cast to fix it.

Daniel,

if the base class does not have an interface that exhibits the enhanced
behaviour, and if the base class is not under your control (hence cannot
be enhanced to add the new interface) you have no choice but to use
dynamic_cast to determine whether your object exhibits the behaviour you
require.

I'd be interested to know how many projects you have worked on that
you've never had to break the ideal design of the language, just to get
something done.

Andy
 
D

Daniel T.

Ian Collins said:
Daniel said:
There are occasions when the interface forces your hand. For example
when implementing the W3C Document Object Model, where all of the
container types are collections of the the base object (Node). Node is
seldom used, most containers end up storing derived objects (Elements or
Attributes) that extend the functionality of Node.

Say you have an element type with an attribute you want to use (the link
in an XHTML anchor element for instance) and you wish to process all of
these elements in a document. The DOM interface provides a means of
extracting a list of them by name, but that list is a list of Nodes and
Node doesn't even have attributes!

I prefer to be able to write something like

dom::NodeList anchors(document.getElementsByTagName("A"));

html::Anchor a(anchors[n]);

and let the library do the conversion from Node to Anchor under the
hood. One benefit of using dynamic_cast is the conversion will fail if
the Node isn't the expected type.

I will be happy to grant that if you are coding in a representational
style instead of Object Oriented, you may very well have to use
dynamic_cast. I don't code that way, nor do any of the libraries I
use.

OK, given an interface with the restrictions I mentioned above, how
would you code it in an "OO" style?

If I was forced to use an interface with the restrictions you mention,
then I couldn't code in an OO style. So I'm not sure how to answer your
question.
 
I

Ian Collins

Daniel said:
If I was forced to use an interface with the restrictions you mention,
then I couldn't code in an OO style. So I'm not sure how to answer your
question.

So your original contention

"Because I program using OO idioms, not representational ones. Or to put
it another way, I specifically design my programs such that I don't need
to use dynamic_cast... even occasionally."

Doesn't hold water?
 
D

Daniel T.

Andy Champ said:
if the base class does not have an interface that exhibits the
enhanced behaviour, and if the base class is not under your control
(hence cannot be enhanced to add the new interface) you have no
choice but to use dynamic_cast to determine whether your object
exhibits the behaviour you require.

Unless the part of the program that is creating the objects is *also*
out of your control, and it is dumping the static information about the
object's type, you are not required to use dynamic_cast. Such a design
is, IMHO sub-standard.

Have you ever noticed all the arrows in UML diagrams? Arrows leading
from users to that which is used? 'dynamic_cast' goes against those
arrows.
I'd be interested to know how many projects you have worked on that
you've never had to break the ideal design of the language, just to
get something done.

And I'd be interested to know how few projects you worked on before you
started breaking ideal design principles willy nilly. (I'm *kidding*! :)

Seriously though, I've been programming professionally in C++ since 1998
and have credit in the release of over a dozen titles. For many of them
I was involved in the design of the framework that other team members
relied on. I am currently the most senior developer at my job, and so
often find myself as the chief code reviewer and example setter. I.E., I
can't afford to break the rules of good design without loosing
credibility. :)

If you feel you absolutely must use dynamic_cast in a particular
situation, by all means do so. The OP asked how many times have I used
it, and the answer is never in professional code. I, of course, have
used it when helping others learn its mechanics, toy programs and
tutorials...
 
D

dave_mikesell

Good discussion. I have a perhaps naive question directed to any and
all in this thread. If you have to work with a hierarchy of classes
that extend the base with their own methods, would it be reasonable to
create a parallel adapter hierarchy with no-op methods on classes that
have no "real" implementation of the individual leaf methods? Or does
this just mask the original design problem with a fat interface?
 
D

Daniel T.

Ian Collins said:
So your original contention

"Because I program using OO idioms, not representational ones. Or
to put it another way, I specifically design my programs such that
I don't need to use dynamic_cast... even occasionally."

Doesn't hold water?

Of course it does, it's completely true. I don't design interfaces with
the restrictions you mentioned, and I don't use interfaces with such
restrictions. I program using OO idioms, not representational ones. I
design my programs such that I don't need to use dynamic_cast.
 
J

Juha Nieminen

Jeff said:
Me: "Why not store ten different collections for D0-D9? They would be
statically type-safe."

What if the class which handles the collection is in a library which
you cannot modify, or which you can but doing so is very laborious (more
laborious than simply using dynamic_cast)?

What if the different objects need to be in a certain order inside the
collection? (For example, assume that you need to perform operations
like "from all the objects in the collection which fulfill certain
conditions, give me the one which is closest to the end of the
collection.") How would you manage this if you had 10 collections
instead of one?
 
D

Daniel T.

Good discussion. I have a perhaps naive question directed to any and
all in this thread. If you have to work with a hierarchy of classes
that extend the base with their own methods, would it be reasonable to
create a parallel adapter hierarchy with no-op methods on classes that
have no "real" implementation of the individual leaf methods? Or does
this just mask the original design problem with a fat interface?

If I understand you right, you are describing the visitor pattern. I
don't use it either.

Virtual no-op member-functions, if they are not used by the class they
are defined in, are also IMHO indicative of poor design. If they *are*
used by the class they are defined in (i.e., the template method
pattern,) then they are useful and generally a good idea.
 
D

Daniel T.

Juha Nieminen said:
Jeff Schwab wrote:
Me: "Why not store ten different collections for D0-D9? They
would be statically type-safe."

What if [you are forced to work with classes that are designed
poorly?]

That's what dynamic_cast is there for.
 
I

Ian Collins

Daniel said:
Of course it does, it's completely true. I don't design interfaces with
the restrictions you mentioned, and I don't use interfaces with such
restrictions. I program using OO idioms, not representational ones. I
design my programs such that I don't need to use dynamic_cast.

Luck boy. Unfortunately I have clients who sometimes ask me to
implement an interface that does not fit well with OO idioms.

One of these days I might have your luxury of picking and choosing
projects based on idioms....
 
D

Daniel T.

Ian Collins said:
Luck boy. Unfortunately I have clients who sometimes ask me to
implement an interface that does not fit well with OO idioms.

One of these days I might have your luxury of picking and choosing
projects based on idioms....

I don't pick the projects, I pick the design. Do you write a lot of
applications that use databases? I have heard that dynamic_cast is much
more prevalent in those types of programs.
 
I

Ian Collins

Daniel said:
I don't pick the projects, I pick the design. Do you write a lot of
applications that use databases? I have heard that dynamic_cast is much
more prevalent in those types of programs.

Yes, frequently. I haven't had to resort to dynamic_cast in that domain.

A quick search of my active code base reveals two instances of
dynamic_cast, const and non-const conversion operations in my DOM code!
 
G

Greg Herlihy

  The other alternative is to use the callback mechanism and
dynamic_cast, for example like this:

void MyClass::doSomethingToPrimitive(Primitive* p)
{
    Circle* c = dynamic_cast<Circle*>(p);
    if(c)
    {
        // Do something to the circle
    }

}

  Ugly? Maybe. But IMO less ugly, and especially less laborious than the first option.

Does the callback really care whether the class of the object (or one
of its base classes) is named "Circle" - or is the callback code more
interested in whether the shape represented by the object - is a
circle (or at least some shape with "circle-like" qualities)?

Almost certainly, the name of the object's class type is not the real
test being applied in the above example. It is easy to image that a
French C++ programmer might decide to name the very same type
differently - "Cercle" perhaps. So in reality, the name of the class
is acting as a kind of proxy for some other aspect or combination of
aspects embodied by the type.

So one of the drawbacks with using dynamic_cast<> and the class name
as a proxy for the properties that actually matter to the call back
routine, is that the dynammic_cast leaves us with little idea what it
is about a circle exactly - that sets it apart from other primitive
types as far as this callback routine is concerned. Whereas, if the
dynamic cast<> were replaced by any of the following member function
calls:

if (p->getNumberOfSides() == 1)
..
if (p->isRound())

or even:

if (p->getShapeType() == kCircleShapeType)

we would have a much better idea what the callback routine is actually
doing with these primitive types. And knowing what a routine is trying
to do goes a long way toward making that routine easy-to-maintain.

Now, calling getShapeType() in the last example might appear to be the
"moral equivalent" to a dynamic_cast<Circle*>. Certainly, the test
being performed is inelegant. Nonetheless, calling a member function
to test for circle types is nonetheless a marked improvement over
calling dynamic_cast<> to do the same.

For one, calling getShapeType() is likely to be much more efficient
than calling dynamic_cast<> (in fact getShapeType() does not
necessarily even have to be a virtual method). Moreover, by using the
class's own interface to distinguish between different types of
objects, we now have a much better understanding of the criteria being
applied. We can conclude from the test for circle type object that the
client requires that the shape not merely be round or one-sided - but
that the object's class type must represent a perfect circle.

Greg
 
B

Bo Persson

Andy said:
Daniel,

if the base class does not have an interface that exhibits the
enhanced behaviour, and if the base class is not under your control
(hence cannot be enhanced to add the new interface) you have no
choice but to use dynamic_cast to determine whether your object
exhibits the behaviour you require.

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?

Then making dynamic_cast easier to use is perhaps not the best
solution. :)



Bo Persson
 
J

James Kanze

if the base class does not have an interface that exhibits the
enhanced behaviour, and if the base class is not under your
control (hence cannot be enhanced to add the new interface)
you have no choice but to use dynamic_cast to determine
whether your object exhibits the behaviour you require.

If if the base class is under your control, the extended
interface may not logically belong there. Dynamic_cast has its
place in good OO design; it just shouldn't be abused.
 
J

James Kanze

Ian Collins said:
Daniel said:
There are occasions when the interface forces your
hand. For example when implementing the W3C Document
Object Model, where all of the container types are
collections of the the base object (Node). Node is
seldom used, most containers end up storing derived
objects (Elements or Attributes) that extend the
functionality of Node.
Say you have an element type with an attribute you want
to use (the link in an XHTML anchor element for instance)
and you wish to process all of these elements in a
document. The DOM interface provides a means of
extracting a list of them by name, but that list is a
list of Nodes and Node doesn't even have attributes!
I prefer to be able to write something like
dom::NodeList anchors(document.getElementsByTagName("A"));
html::Anchor a(anchors[n]);
and let the library do the conversion from Node to Anchor
under the hood. One benefit of using dynamic_cast is the
conversion will fail if the Node isn't the expected type.
I will be happy to grant that if you are coding in a
representational style instead of Object Oriented, you may
very well have to use dynamic_cast. I don't code that way,
nor do any of the libraries I use.
OK, given an interface with the restrictions I mentioned
above, how would you code it in an "OO" style?
If I was forced to use an interface with the restrictions you
mention, then I couldn't code in an OO style. So I'm not sure
how to answer your question.

So how would you design a DOM interface in your definition of OO
style?

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. You basically have the choice between moving all
of the possible derived class interfaces down to the base class,
or using dynamic_cast to get at them. I find the latter
preferable (and more OO) than the former.

Note that in general, what I would avoid is dynamic_cast to a
concrete type (although there too, there might be exceptions,
e.g. when implementing a surrogate for multiple dispatch).
Generally, if I need an extended interface, I define an extended
interface (using virtual inheritance from the base interface)
with the added functionality. Concrete classes then derive from
the extended interface (or from several extended interfaces, if
they offer several extensions). Users interogate whether an
instance supports the extension by using dynamic_cast to the
extended interface.
 
J

Juha Nieminen

Daniel said:
Juha Nieminen said:
Jeff Schwab wrote:
Me: "Why not store ten different collections for D0-D9? They
would be statically type-safe."
What if [you are forced to work with classes that are designed
poorly?]

That's what dynamic_cast is there for.

How is it a poor design that a collection has object in a certain
order (in a context where this order is relevant)?

One good example of a collection where order is relevant is a
collection of screen elements: The order of these screen elements in the
collection is the order in which they are drawn on screen, which
determines which elements are drawn on top of the others.

I can't even begin to imagine how you could easily determine the
drawing order of screen primitives if each primitive was in a
type-specific container. You would need an additional container which
contains the pointers in the order in which the primitives should be
drawn, duplicating the amount of data stored and making the whole system
quite a lot more complicated to manage (for example swapping the drawing
order of two given primitives becomes needlessly complicated).
 
D

Daniel T.

Juha Nieminen said:
Daniel said:
Juha Nieminen said:
Jeff Schwab wrote:
What if [you are forced to work with classes that are designed
poorly?]

That's what dynamic_cast is there for.

How is it a poor design that a collection has object in a certain
order (in a context where this order is relevant)?

Why would you even ask the question? Nobody has claimed that the above
was poor design.
One good example of a collection where order is relevant is a
collection of screen elements: The order of these screen elements
in the collection is the order in which they are drawn on screen,
which determines which elements are drawn on top of the others.

I can't even begin to imagine how you could easily determine the
drawing order of screen primitives if each primitive was in a
type-specific container. You would need an additional container
which contains the pointers in the order in which the primitives
should be drawn, duplicating the amount of data stored and making
the whole system quite a lot more complicated to manage (for
example swapping the drawing order of two given primitives becomes
needlessly complicated).

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.

Trying to go backwards along an inheritance arrow is as poor design as
trying to go backwards along an association arrow. The only difference
is that with the former, the language provides a primitive to allow it,
while with the latter, it doesn't. Just because the primitive is
provided, doesn't mean it should be used willy nilly.
 
D

Daniel T.

Ian Collins said:
Yes, frequently. I haven't had to resort to dynamic_cast in that
domain.

A quick search of my active code base reveals two instances of
dynamic_cast, const and non-const conversion operations in my DOM
code!

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.
 

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

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,240
Members
46,828
Latest member
LauraCastr

Latest Threads

Top