dynamic_cast is ugly!

K

Kai-Uwe Bux

James said:
You've got it backwards. Why do you want to encumber all the
rest of the Shapes with something that is only relevant to
Squares?

I think what Daniel is getting at is that you shouldn't have to know whether
your request is only relevant to Squares. You should just notify all
objects in your collection that a certain condition obtains and those
objects will decide autonomously whether they have to respond. It might
happen (somewhat by accident) that only Squares feel the need to react, but
that is nothing that client code should need to care about. In other words,
if you find yourself dealing with a collection of objects and you want to
send notifications that cannot be _understood_ by all objects in the
collection, then there is something fishy. However, not all objects that
understand the message need to react in the same way (or even react at
all).


Best

Kai-Uwe Bux
 
J

Juha Nieminen

Dmitry said:
A third alternative would be to convert std::vector<Shape*> to
std::vector<Square*>. What is the least evil depends on each concrete
situation.

But then you lose the possibility of having more than one shape type
in that container. How is that a viable solution?
(BTW, using pointers is also bad design. The language should allow
construction of containers of polymorphic elements.)

Well, we have to live with what C++ has to offer. I don't think that's
the main issue here.
 
N

Nick Keighley

Don't try to *do* things to objects. Objects are supposed to do for
themselves, clients *notify* the objects of things, they don't order
them around.


What is it you are trying to tell squares that you don't want any other
shapes to know about? Why the big secret?

"colour all the squares yellow"

If the shapes example is a bit fanciful make it a UML editor.
I want all the interfaces to stand out by changeing their
colour and my model is huge.

"colour all the interfaces yellow"
 
J

Juha Nieminen

Daniel said:
Don't try to *do* things to objects. Objects are supposed to do for
themselves, clients *notify* the objects of things, they don't order
them around.

How exactly do you propose to do that? Perhaps have a virtual function
in the base class like:

virtual void performAction(const std::string& action,
const std::string& parameters);

and then have the derived classes parse those parameters?

I think I prefer dynamic_cast.
What is it you are trying to tell squares that you don't want any other
shapes to know about? Why the big secret?

It's not like they must not know about it. It's just that it can be
something which only Square can rationally implement. The others have no
rational use for that functionality, and it would only be useless
clutter for their public interface to have such a function (which does
nothing).

As for an abstract "perform this action if you support it" function
like above... No thanks? For one, it's quite inefficient.
 
M

Matthias Buelow

Daniel said:
The good news, and bad news, with a dynamic-typed language, is that less
of the design is actually written out in code. Because of this, the code
can be written faster, and the the code is harder to figure out after it
is written.

Why is it harder to figure out? If you mean instead, harder to
understand what is actually going on "under the hood", then yes. This
isn't obvious unless you know how the compiler/interpreter works.
However, the idea is, that these are irrelevant details that can be
ignored. Actually, it's the same with languages like C++ -- do you
really want to have to care about how the compiler implements virtual
functions, how certain templates expand, etc.? The problem is, sometimes
you need to know, if only for performance reasons, to avoid copying etc.
So it isn't really that much different for a "statically" typed language
like C++.
 
D

dave_mikesell

You've got it backwards. Why do you want to encumber all the
rest of the Shapes with something that is only relevant to
Squares?

You don't. Whatever functionality that we're talking about here
belongs either in Square or some other interface (not Shape) that it
implements. The problem here is that Shape, in this example, is too
generic to be very useful.

This is starting to go around in Circles.
 
D

dave_mikesell

But then you lose the possibility of having more than one shape type
in that container. How is that a viable solution?

If you want to maintain substitutability in your class hierarchy
through the abstract base, you wouldn't create a heterogeneous
container. Depends on your design goals I guess.
 
D

dave_mikesell

"colour all the squares yellow"

If the shapes example is a bit fanciful make it a UML editor.
I want all the interfaces to stand out by changeing their
colour and my model is huge.

"colour all the interfaces yellow"

You can solve that by putting homogeneous elements in their own
container for just such operations If you want to globally change the
line style of connectors, operate on the Connector collection, don't
rifle through the generic one interrogating each object.
 
D

Daniel T.

Daniel T. wrote:

How exactly do you propose to do that? Perhaps have a virtual
function in the base class like:

virtual void performAction(const std::string& action,
const std::string& parameters);

and then have the derived classes parse those parameters?

No, that is not how to do it. Quit telling objects what to do, try to
decentralize your thinking and let objects do for themselves.

To borrow Nick Keighley's example (which Dave Mikes answered
admirably,) don't parse through a container of Shapes, looking for all
the squares and telling them to turn yellow (even if that is only
something a square can do.) Let all the shapes know that a "turn
yellow" request has been made for squares. Some non-squares may be
interested (maybe lines connected to squares want to be in a
contrasting color,) maybe some squares don't want to change color
(they have been asked specifically to stay blue.)

Also, refer to Kai-Uwe Bux's post from this morning... Very well put!
 
D

Daniel T.

I think what Daniel is getting at is that you shouldn't have to know whether
your request is only relevant to Squares. You should just notify all
objects in your collection that a certain condition obtains and those
objects will decide autonomously whether they have to respond. It might
happen (somewhat by accident) that only Squares feel the need to react, but
that is nothing that client code should need to care about. In other words,
if you find yourself dealing with a collection of objects and you want to
send notifications that cannot be _understood_ by all objects in the
collection, then there is something fishy. However, not all objects that
understand the message need to react in the same way (or even react at
all).

Exactly! Well put. The moment you find you *need* to use dynamic_cast
in order for the program to work correctly, is the moment you learn
that the program's design is flawed. Mmaybe you don't have a choice,
maybe it isn't even your design. If that is so, then use dynamic_cast
and be done with it.
 
D

Dmitry A. Kazakov

Dmitry A. Kazakov wrote:

But then you lose the possibility of having more than one shape type
in that container. How is that a viable solution?

Basically std::vector<Square*> is a view on an instance of
std::vector<Shape*>. The view is implemented using by-value semantics, so a
physical conversion is necessary [*]. When you know the constraint you
convert to that view (container of Squares) and pass it to a method which
statically knows that the container has only Squares. If you wanted it in
the in-out mode you could do it with copy-in / copy-out.
 
J

Juha Nieminen

It almost sounds like you are talking about a delegation paradigm.
Note that we are talking about C++ here, not objective-C. C++ does not
support delegation natively.
Exactly! Well put. The moment you find you *need* to use dynamic_cast
in order for the program to work correctly, is the moment you learn
that the program's design is flawed.

It's very easy to say that a design is flawed. It's much harder to
actually suggest a better design (which is not orders of magnitude more
awkward and difficult to use than just using dynamic_cast).
 
J

Juha Nieminen

Daniel said:
To borrow Nick Keighley's example (which Dave Mikes answered
admirably,) don't parse through a container of Shapes, looking for all
the squares and telling them to turn yellow (even if that is only
something a square can do.) Let all the shapes know that a "turn
yellow" request has been made for squares.

You have still not answered my question: How do you propose I do that?
It almost sounds like you are proposing a delegation paradigm, something
which C++ doesn't support directly.

Note that some operations may be required to be performed in a precise
order. It may not be enough to simple tell objects of certain type to do
something: It may be necessary for them to do that in a given order (eg.
the order in which they are in the container which has all the shapes).

Also, in some cases the operation which has to be performed *with* the
objects might not be doable *by* the objects. The operation to be
performed with the objects might read some values from the objects or
whatever, but whatever it's doing might not be doable by the objects
themselves (eg. because the objects themselves have no access to
something required to perform the operation).
 
J

Juha Nieminen

Dmitry said:
Dmitry A. Kazakov wrote:
But then you lose the possibility of having more than one shape type
in that container. How is that a viable solution?

Basically std::vector<Square*> is a view on an instance of
std::vector<Shape*>. The view is implemented using by-value semantics, so a
physical conversion is necessary [*]. When you know the constraint you
convert to that view (container of Squares) and pass it to a method which
statically knows that the container has only Squares. If you wanted it in
the in-out mode you could do it with copy-in / copy-out.

Sorry, I didn't understand that at all. You'll have to explain in
simpler and more concrete terms.
 
H

H. S. Lahman

Responding to Herring...
So now you have to expose that attribute, and the client has to have
access to the ordering relation which was previously encapsulated in the
collection.

It is a problem space property that is important to the requirements and
software solution. It is abstracted as an intrinsic knowledge attribute
of an object. That is basic problem OOA space abstraction.

I also don't follow how it was previously encapsulated in the
collection; the type, which includes the size property, is fully exposed
in the dynamic_cast solution.
And if the number of collections increases beyond two, that "simple
interleaving" doesn't scale well, as it starts to look more and more
like a complete (partial) sort, so really the client, not the
collection, is now responsible for the ordering.

I disagree that it doesn't scale well because the number of machine
instructions executed is going to be essentially the same in either
case, but let's not go there.

The relevant point to the thread is that the client checks the size
attribute rather than the object type via dynamic_cast. Making flow of
control decisions based on problem space properties will always be more
robust during maintenance than making such decisions on 3GL
implementation properties. The goal is to make the application more
maintainable and avoid foot-shooting; not elegance, reduced keystrokes,
minimizing static structure, or even being convenient for the developer.



--
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
 
D

Daniel T.

You have still not answered my question: How do you propose I do that?
It almost sounds like you are proposing a delegation paradigm, something
which C++ doesn't support directly.

There are any number of ways to do it, Dave Mikes mentioned one,
another would be to have a virtual function in Shape that lets Shapes
know that a "change rectangles to yellow" request has been made.

Again, there are so many correct ways to do it, that without a
spicific problem domain, I can't just spout out some answer that is
guaranteed to be correct for all domains.
Note that [the coder might be dealing with a poorly designed interface...]

If that's the case, then dynamic_cast may be the only solution.
 
D

Dmitry A. Kazakov

Dmitry said:
Dmitry A. Kazakov wrote:
A third alternative would be to convert std::vector<Shape*> to
std::vector<Square*>. What is the least evil depends on each concrete
situation.
But then you lose the possibility of having more than one shape type
in that container. How is that a viable solution?

Basically std::vector<Square*> is a view on an instance of
std::vector<Shape*>. The view is implemented using by-value semantics, so a
physical conversion is necessary [*]. When you know the constraint you
convert to that view (container of Squares) and pass it to a method which
statically knows that the container has only Squares. If you wanted it in
the in-out mode you could do it with copy-in / copy-out.

Sorry, I didn't understand that at all. You'll have to explain in
simpler and more concrete terms.

Pass std::vector<Shape*> where std::vector<Square*> expected.
 
I

Ian Collins

Daniel said:
There are any number of ways to do it, Dave Mikes mentioned one,
another would be to have a virtual function in Shape that lets Shapes
know that a "change rectangles to yellow" request has been made.
Which, if you excuse the pun, completes the circle on this thread.

What this thread shows is that unlike some other languages, there is no
"correct way" to handle this problem in C++. There are those who
passionately believe in one way and those who equally passionately
believe in another.

So we end up arguing round and round in circles.

Walk away and rejoice in the flexibility of the language!
 
C

coal

No, that is not how to do it. Quit telling objects what to do, try to
decentralize your thinking and let objects do for themselves.

To borrow Nick Keighley's example (which Dave Mikes answered
admirably,) don't parse through a container of Shapes, looking for all
the squares and telling them to turn yellow (even if that is only
something a square can do.) Let all the shapes know that a "turn
yellow" request has been made for squares. Some non-squares may be
interested (maybe lines connected to squares want to be in a
contrasting color,) maybe some squares don't want to change color
(they have been asked specifically to stay blue.)

Hmm.
With CORBA, ordering objects around is the norm. A distributed
object model blindly translates the voodoo from one context to
another. I think this is a weakness of CORBA that often works it's
way into the designs of those using CORBA. On the other
hand, I have no idea what it means to "let objects do for
themselves," where the objects in question are inanimate.
Also, refer to Kai-Uwe Bux's post from this morning... Very well put!

I found his post helpful as well. One small thing about that post: he
used the word 'obtains' where it seemed like the word he wanted was
exists or pertains.

Brian Wood
Ebenezer Enterprises
www.webebenezer.net
 
A

Andreas Dehmel

There are any number of ways to do it, Dave Mikes mentioned one,
another would be to have a virtual function in Shape that lets Shapes
know that a "change rectangles to yellow" request has been made.

Since in a proper OO design, Shapes shouldn't know anything about its
specializations, any methods referring to specializations such as
rectangles are by definition very poor OO design.
Again, there are so many correct ways to do it, that without a
spicific problem domain, I can't just spout out some answer that is
guaranteed to be correct for all domains.

If you call cluttering the base interface with a plethora of highly
specialization-biased methods a ``correct way'' to deal with this problem
then any further discussion is pointless. In my book that's by several
orders of magnitude worse than the occasional dynamic_cast.

I think few around here would argue that one should minimize the use
of dynamic_cast and in particular not ``design'' anything that requires
it even for its most basic functions. But completely dismissing it
like you did and the ``correct'' workarounds you suggested are mere
fundamentalism as far as I'm concerned.



Andreas
 

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,830
Latest member
HeleneMull

Latest Threads

Top