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
> How many times have you written
>
> someType* var = dynamic_cast<someType*>(someOtherPtr);
In professional code? Never.
Why not?
Andy
Because it pretty much always indicates a design flaw. Polymorphism is
the preferred idiom.
Of course, sometimes design flaws are unavoidable, particularly when
not all the code involved is under your control.
--
Micah J. Cowan
Programmer, musician, typesetting enthusiast, gamer...
http://micah.cowan.name/
The C++ cast operators, including dynamic_cast<>, were deliberately
designed to have an unwieldy syntax precisely to discourage C++
programmers from using them. Why, after all, does a program need to
perform a downcast to someType? Either "SomeOtherPtr" should have been
a pointer to a someType object all along - or class-specific behavior
that the client needs should have been modeled inside the class
implementation itself.
Greg
In my case, it isn't the code, it's the data structures that the
objects/classes represent that triggered off me doing this so much.
However, in the general case where you have classes that exhibit very
similar behaviour you are going to want to treat them the same most of
the time, calling common interfaces. Just occasionally you want to know
which of the specialised classes you have, in order to invoke the
specialised behaviour. That's when dynamic_cast comes in.
Greg Herlihy wrote:
> The C++ cast operators, including dynamic_cast<>, were deliberately
> designed to have an unwieldy syntax precisely to discourage C++
> programmers from using them. Why, after all, does a program need to
> perform a downcast to someType? Either "SomeOtherPtr" should have been
> a pointer to a someType object all along - or class-specific behavior
> that the client needs should have been modeled inside the class
> implementation itself.
>
> Greg
I'd love to see a reference on that. I'm guessing it was just easier
for the compiler designers not to have to affect the right side with the
object on the left side of the operator.
With a raw pointer, this works fine. If the object on the LHS has
multiple constructors I can imagine the compiler getting a bit confused
- which is another reason for keeping the traditional, clumsy, syntax at
least some of the time, as it *forces* the type of conversion.
Without templates, you can't do my trick, and templates are a late
addition to the language.
Andy
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.
I think there are a few cases where trying to avoid dynamic_cast at
all costs becomes counter-productive, and the code would only get uglier.
For example, let's assume you have some kind of primitive manager
which can contain all types of primitives (ie. it contains pointers to
objects derived from some 'Primitive' base class).
For the user to be able to perform some operations to those primitives
when the manager so requests, there are basically two options: The
manager either calls a virtual function defined in the 'Primitive' base
class (which the derived classes can implement), or the manager calls a
callback function given to it by the user, giving this callback function
a reference/pointer to the object (this reference/pointer has to be of
type 'Primitive', of course, because the actual object can be of any type).
The first option would be the nicest, but it becomes awkward in some
cases. For example, if the operation to be performed requires something
the object itself cannot do. This would mean that the object would have
to have a pointer to the actual module which can do that operation, and
it has to call a function through that pointer, and it has to have
permission to do so (the function must be public, or the object must be
a friend of the module).
This also means you can't have pre-made primitives (eg. provided by
the same library which provides the primitive manager) which can simply
be instantiated and given to the manager. At the very least you will
have to create your own class derived from this pre-made primitive, and
in it you will have to implement the store-pointer-call-function
functionality. This may quickly become laborious if you want to use lots
of different pre-made primitives.
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.
The usual reason to avoid dynamic_cast is that it can fail: This
happens if the object behind the pointer is not of the type being casted
to. However, in this case this is not a problem at all, and in fact part
of the very functionality of the system: The dynamic_cast is actually a
"check" to see if the given object was of a certain type (and if it was,
then some operation is done to it).
Sure, with many objects you end up with a large amount of dynamic
casts and if-blocks, but the alternative is not any less laborious. One
could even argue that the alternative is less readable because the
actual functionality is dispersed and not concisely located at one
place. It all depends on the actual situation, I suppose.
Do you have any reference to corroborate this?
One could argue that the different casts were made keywords to help
making the code more readable (and make it easier to, for example, find
the places where a certain type of cast is being done).
I guess it is one of those urban legend.
> One could argue that the different casts were made keywords to help
> making the code more readable (and make it easier to, for example, find
> the places where a certain type of cast is being done).
IMO you are right.
The cast the most likely to be used is the static_cast<> and it is not
used that much: mainly when the implicit cast is dangerous or ambiguous.
The const_cast<> and reinterpret_cast<> can yield UB unless used with
care and dynamic_cast<> is so seldom used that it is surprising to see it.
Michael
Agreed. If those were our only two options, then you are 100% correct.
Fortunately, there are other options. This is exactly why OO books warn
against "manager" objects.
Thanks for not giving even a hint of these other options and were to
find more info about them.
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.
See Design & Evolution, or Stroustrup's FAQ.
http://www.research.att.com/~bs/bs_faq2.html#static-cast
"An ugly operation should have an ugly syntactic form."
>> One could argue that the different casts were made keywords to help
>> making the code more readable (and make it easier to, for example, find
>> the places where a certain type of cast is being done).
>
> IMO you are right.
>
> The cast the most likely to be used is the static_cast<> and it is not
> used that much: mainly when the implicit cast is dangerous or ambiguous.
>
> The const_cast<> and reinterpret_cast<> can yield UB unless used with
> care and dynamic_cast<> is so seldom used that it is surprising to see it.
I see dynamic_cast all the time. The ensuing conversation between me
and the original coder usually is something like this:
<conversation>
Coder: "I'm dynamically allocating a sequence of objects, each of which
may be of any of ten different types D0-D9, all implementing a common
run-time interface B (base class with virtual methods). I allocate the
objects with operator new, and store pointers to them in a monolothic
collection of pointers-to-B. If I later need all the objects of type
D1, I walk the list and dynamic cast every single element to find out
whether it's of type D1."
Me: "Why not store ten different collections for D0-D9? They would be
statically type-safe."
Coder: "Usually, I have to work with all the objects, and I don't want
to have to traverse ten different collections."
Me (ashamed to be encouraging premature optimization): "Dynamic cast is
slow."
Coder: "Ten lists have more overhead than one list. It's a trade-off."
Me: "You're not letting the compiler help you."
Coder: "I don't need the compiler's help here. The run-time environment
does what I need. Why should I go out of my way to get the compiler's
approval? I've been doing this for ## years. I know what I'm doing."
Me: (Thinks about explaining the manifold potential safety and
performance blessings that come from moving computation to compile-time,
and danger of violating an excellent rule of thumb for no good reason.
Decides it's not worth further irritating Coder.) OK, thanks very much
for going over that with me. I appreciate your time.
</conversation>
In the end, I just request that there be documented APIs between
different parts of the code, so that I will have a decent shot at
figuring out who's code is causing the bugs I know we'll be seeing soon.
Even that is an up-hill battle, but modularity is lower-hanging fruit
than compile-time safety. I think programmers just continue to believe
whatever they were told by their Computer Science professors, without
ever really thinking for themselves; "modularity" makes the list of
things covered in typical undergraduate courses, but "static type
safety" does not. What really irritates me is that kids coming out of
school seem to have even less appreciation for static type safety than
the old-timers, so there's not a whole lot of hope in sight. The Python
community in particular seems to believe static typing is about as
obsolete as magnetic core memory. It's depressing.
For a reference, please see this Bjarne Stroustrup post to comp.std.c+
+ (and which also contains a link to his C++ faq):
http://groups.google.com/group/comp.std.c++/msg/b8974d7d5f71ca5a
Greg
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.
--
Ian Collins.
All I can say to that is that I have never had my hand forced. :-) The
question with the above is, do you know statically (i.e., at compile
time) the types of the objects? Even with generic containers like you
mention, if you only put in objects of type X, then you know that all
objects in the container are of type X.
No, that's the nasty bit.
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.
--
Ian Collins.
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.
> [...]
> <conversation>
> Coder: "I'm dynamically allocating a sequence of objects, each of which
> may be of any of ten different types D0-D9, all implementing a common
> run-time interface B (base class with virtual methods). I allocate the
> objects with operator new, and store pointers to them in a monolothic
> collection of pointers-to-B. If I later need all the objects of type
> D1, I walk the list and dynamic cast every single element to find out
> whether it's of type D1."
:-o
That being said, I wonder if there is a technical reason why const static
virtual member variables are not allowed. For example, this does not
compile:
class base {
public:
int i;
virtual static const enum types {
type_base = 0, type_1, type_2, type_3, type_n
} id = type_base;
};
class sub1 : public base {
static const types type = type_1;
};
But why, I ask you, should the vtable only contain method pointers? The one
problem I see in syntactical: Is the following a pure virtual member or a
member initialised to 0:
class c { virtual static const int i = 0; };
Only half serious,
Paul
>>>> 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?
--
Ian Collins.
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
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.
>> 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 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?
--
Ian Collins.
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...
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
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?
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.
> > 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.
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....
--
Ian Collins.
> 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.
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!
--
Ian Collins.
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
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
> >>> Agreed. If those were our only two options, then you are 100% correct.
> >>> Fortunately, there are other options.
> >> Thanks for not giving even a hint of these other options
> >> and were to find more info about them.
> > 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.
> 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.
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
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.
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).
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.
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 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.
> > 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?
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.
> 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.
> > Dynamic_cast has its place in good OO design; it just
> > shouldn't be abused.
> 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.
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.
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.
So you've never used a generic framework?
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.
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.
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.
> > > Dynamic_cast has its place in good OO design; it just
> > > shouldn't be abused.
> > Just like goto? :-)
> 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;
}
--
Peace
JB
j...@tetrahedraverse.com
Web: http://tetrahedraverse.com
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
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
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.
> > > 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.
> 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...
}
> > 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.
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.
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?
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.
I'm afraid this is just too vague to be of any help to me...
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
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
h...@pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
in...@pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
I create the objects, and use them. However, their intermediate storage
is not under my full control.
> 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 don't see that.
>> 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*! :-)
I'm not. The answer is quite simple - no projects at all. It was in my
first job that it was pointed out to me that while the design we were
going to use was not ideal, and was going to store up trouble in the
long term, the correct design would take so much longer to implement
that if we tried it there would *be* no long term. This is the
principle that XP zealots call "YAGNI".
> 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. :-)
You've allayed my suspicion :) You are neither a student nor an
academic, but do inhabit the real world! I must admit that a dozen
projects in 10 years indicates that they are somewhat smaller than the
ones I am involved with. For example I have been at my current employer
for 7 years, and have worked on 4 projects. One of these has been
cancelled, because the market went away. The other three are still
going. BTW the current one has over 100 classes, even though we've
compromised the design and folded some similar ones together.
Andy
Assembler is impossible without it.
However, I've never written one in C or any of its derived languages,
and I've only seen them in Pascal used as the equivalent to "throw
FatalException".
Andy
Actually, I'm advocating choice 3:
Have the producer provide the type information directly to the
consumer, this doesn't necessarally have to be through the collection.
If the collection's job is to keep track of ordering information, then
that should be its only job. The consumer shouldn't be expected to use
that collection to *also* get all objects of a particular type.
I don't think it is necessarally a good idea to put type information
in the collection, but it is a good idea to only navigate
relationships along the trajectory they were designed to be navigated
along (i.e., follow the UML arrows, don't try to backtrack against
them.) And, I think it is important to follow the "tell, don't ask"
principle. (http://pragmaticprogrammer.com/articles/tell-dont-ask)
Asking the object its type and then telling the object to do something
based on the answer goes against this principle.
Of course it's vague. Without a spicific problem statement, a spicific
answer simply doesn't exist.
Or to put it another way, there are hundreds of ways to get the type
information from the producer to the consumer. Pretend that
dynamic_cast doesn't exist and see what you can come up with in your
particular problem space.
> > 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. :-)
>
> You've allayed my suspicion :) You are neither a student nor an
> academic, but do inhabit the real world!
Actually, I've never been a "student" as in I didn't go to college to
learn programming or C++ and I don't have any sort of CS or IT degree.
Obviously, I am a student in the sense that I have worked hard to
learn my craft.
> I must admit that a dozen projects in 10 years indicates that they
> are somewhat smaller than the ones I am involved with. For example
> I have been at my current employer for 7 years, and have worked on 4
> projects. One of these has been cancelled, because the market went
> away. The other three are still going. BTW the current one has over
> 100 classes, even though we've compromised the design and folded
> some similar ones together.
I'm not sure what to say to the above... My last completed project had
about 550 classes in it, just for the application code itself, not
including the framework which is another 350 classes (not all of which
is used in every project of course.) We usually finish a project
(i.e., from the day we get the first draft of the design doc to the
day we deliver the "gold master") in about 6-9 months. Needless to
say, the design doc is in a state of extreme flux while we are working
on the code, so it is very important that the code itself is very
flexible as well.
I have also worked on several "failed" projects. Strictly speaking,
only two actually failed, but there were others that were prototypes
which never managed to see the light of day.
So, when you say your projects must be bigger than mine because you
only complete 3 in 7 years and they only contain "over 100 classes",
I'm not sure what that means.
> >>> Dynamic_cast has its place in good OO design; it just
> >>> shouldn't be abused.
> >> Just like goto? :-)
> > No. Goto never has its place.
> Yes it does. I can think of several places where it's really
> valuable.
> Assembler is impossible without it.
And Fortran IV:-). (My assemblers didn't have goto. We used
something called B or JMP.)
> However, I've never written one in C or any of its derived
> languages, and I've only seen them in Pascal used as the
> equivalent to "throw FatalException".
Yes. The problem isn't the goto itself; the problem is the
program flow structure. One of the cleanest programs I ever saw
was written in Fortran IV, and of course, it used GOTO. But
only to emulate the structures that were missing in the
language.
In C++, of course, we have all of the structures we need (and
even some we don't), so there's really no place for goto.
Agreed, but you can't really have it both ways. At least
dynamic_cast is like finding a detailed inventory at the top of
the box when you open it (or arguably, in an envelop pasted to
the outside of the box). I rather prefer it to the Smalltalk
solution where you have to ask about each method.
And you still haven't responded to Juta's question about what
you do when the transporter is managing several different
producer/consumer relationships, using different types, and the
order is significant. Keeping the actual objects in separate
containers, one for each type, means managing the order
externally. That's a lot of extra program logic just to avoid
one dynamic_cast at the consumer side.
In the end, of course, it's a trade off. Loosing the type, and
thus needing the dynamic_cast, means that what should have been
a compile time error becomes a runtime error. I've done enough
Java in the past (before Java had templates) to know what that
can cost. But in certain limited cases, it's manageable, and
increasing the code size of the transport mechanism by a factor
of 10, at the same time moving all of its code into templates
(and thus into the header files), isn't free either.
And of course, there are other cases where you might want it as
well: I've used in for version management in the past, and I'm
not alone. You might want to look at java.awt.LayoutManager2,
for example, and how it is used in some of the client classes.
(The name speaks for itself.) Obviously, support for existing
client code---which probably already derived from
java.awt.LayoutManager, and didn't provide the new functions,
but you don't want to limit yourself forever to the interface
that came up in your first design. In my case, I've often used
it in distributed systems---you can't expect 60,000 client
machines to all be updated at the same instant, so your code has
to handle both the older version, and the new, extended version.
(Obviously, the old clients won't ask for features that are only
present in the new version.)
And so on. The fact remains that I've found cases where
dynamic_cast was justified, in the sense that it was more cost
efficient than the alternatives. (With cost being measured in
program readability and maintainability, which basically end up
the equivalent of monitary cost in the end.)
> > Responding to Nieminen...
> > > > 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...
> > 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.
> Actually, I'm advocating choice 3:
> Have the producer provide the type information directly to the
> consumer, this doesn't necessarally have to be through the
> collection.
Isn't this exactlly what dynamic_cast does? What's the
difference between a member function "supportsInterfaceX()" and
dynamic_cast< X* >. Except that providing the member function
means that the base interface must know about the extended
interface.
> If the collection's job is to keep track of ordering information, then
> that should be its only job. The consumer shouldn't be expected to use
> that collection to *also* get all objects of a particular type.
> I don't think it is necessarally a good idea to put type information
> in the collection, but it is a good idea to only navigate
> relationships along the trajectory they were designed to be navigated
> along (i.e., follow the UML arrows, don't try to backtrack against
> them.) And, I think it is important to follow the "tell, don't ask"
> principle. (http://pragmaticprogrammer.com/articles/tell-dont-ask)
> Asking the object its type and then telling the object to do something
> based on the answer goes against this principle.
The problem with this is that it means that everything any
derived class supports must be known and declared in the base
class. And how do you handle the different levels of
functionality? What happens if you tell an object to do
something it's not capable of? (Note that the article you site
is mainly concerned with not exposing mutable state, in order to
reduce coupling. There's nothing "mutable" about the results of
dynamic_cast. In fact, used correctly, it reduce coupling, by
keeping not only the state, but the extended interface, out of
sight except to those who need to know.)
> "modularity" makes the list of
> things covered in typical undergraduate courses, but "static type
> safety" does not. What really irritates me is that kids coming out of
> school seem to have even less appreciation for static type safety than
> the old-timers, so there's not a whole lot of hope in sight.
That's probably because static typing is not a desirable feature in
programming in the first place. It is added complexity, which is
(arguably) sometimes necessary, more necessary with some languages than
others. The languages I know that put emphasis on static type
correctness (for example, ML and Haskell) are extremely unwieldy to
program in. No fun at all. Nobody I think, not even here, would say,
"oh, I miss static typing. It was so neat." It's a burden on the
programmer best avoided if one can. That's why schools and universities
probably don't put much importance on it in programming courses (at
least not the good ones). It's just ballast, nothing more. Sometimes you
need the ballast, often you don't.
> The Python
> community in particular seems to believe static typing is about as
> obsolete as magnetic core memory. It's depressing.
While I'm not a Python fan, they're correct here (imho).
> I don't think it is necessarally a good idea to put type information
> in the collection, but it is a good idea to only navigate
> relationships along the trajectory they were designed to be navigated
> along (i.e., follow the UML arrows, don't try to backtrack against
> them.)
That was pretty much the point I was trying to make with the caveat.
Including object type information explicitly in the collection is only
slightly better than navigating generalizations with dynamic_cast.
There has to be some way to describe the problem context that determines
which particular object type is relevant to the client at the time of
the collaboration. The design problem is to abstract that problem
context into a solution element -- like a context variable or dedicated
relationship. Then the client and collection use the problem context
during all collaborations. One can then isolate and encapsulate the
mapping of problem context to OOPL implementation types around
instantiation rather than collaboration.
>> Actually, I'm advocating choice 3:
>> Have the producer provide the type information directly to the
>> consumer, this doesn't necessarally have to be through the
>> collection.
>
> Isn't this exactlly what dynamic_cast does? What's the
> difference between a member function "supportsInterfaceX()" and
> dynamic_cast< X* >. Except that providing the member function
> means that the base interface must know about the extended
> interface.
The difference is that in a 3GL type system the interface defines what
the object is while dynamic_cast is just a check to determine what the
object is.
dynamic_cast is bad because relationship navigation should always
guarantee that the right objects (types) are there for a collaboration.
In a well-formed OO application one should never have to check whether
the object on the other end of the relationship path is the right object
for the collaboration. IOW, it is the job of relationship instantiation
to ensure that the right object is *always* on the other end of the
relationship path when it is time to collaborate.
dynamic_cast exists in C++ because there is no other facility for
dispatching from an external stream of objects of different types in a
subsystem interface. But that is the only place that it should be used.
It is especially bad to use it to navigate generalizations to determine
the subtype. OO generalization relations are not navigable because a
single object instantiates the entire limb of the generalization, so
there is nothing to navigate; the object in hand is what it is. So it is
either the right object to collaborate with or not. If not, then there
is a problem with the way one got to the object in hand.
If one accesses the tree through a supertype, then it is an abuse of
polymorphic dispatch to use dynamic_cast to qualify that dispatch by
accessing properties that are not common to all descending types.
Polymorphic dispatch *depends* on all descendants being substitutable.
If one needs specific properties one needs to access the generalization
at the level where those properties are common to all descendants. IOW,
one needs to implement and instantiate relationships to the proper level
of property definition in the generalization for the collaborations.
Not quite. Don't forget Fortran 4's main control structure was the
"arithmetic if", which could jump backwards ;-/
If-then-else didn't appear until Fortran 77.
--
Richard Herring
> On Mar 9, 4:49 pm, Andy Champ <no....@nospam.com> wrote:
>> James Kanze wrote:
>> > On 8 mar, 18:51, "Daniel T." <danie...@earthlink.net> wrote:
>> >> On Mar 8, 5:08 am, James Kanze <james.ka...@gmail.com> wrote:
>
>> >>> Dynamic_cast has its place in good OO design; it just
>> >>> shouldn't be abused.
>
>> >> Just like goto? :-)
>
>> > No. Goto never has its place.
>
>> Yes it does. I can think of several places where it's really
>> valuable.
>
>> Assembler is impossible without it.
>
> And Fortran IV:-). (My assemblers didn't have goto. We used
> something called B or JMP.)
>
>> However, I've never written one in C or any of its derived
>> languages, and I've only seen them in Pascal used as the
>> equivalent to "throw FatalException".
There are several cases where goto is quite useful in C; particularly
for "exception handling" within a function, where using goto or
similar unstructured mechanisms to jump to a single point of
cleanup-and-return, are often more readable than the alternatives.
Some of the spots where it is useful in C are still useful in C++,
such as jumping to an outer loop; of course, this is avoidable
with the use of special "condition" variables; but IMO such variables
are more annoying to read than the jump they were meant to avoid.
OTOH, I've run into such a situation _maybe_ once, myself, so
perhaps it doesn't tend to crop up too often.
For all that people complain of goto leading to spaghetti code, my
experience is that spaghetti code is just as easy/common _without_
leaving the confines of "structured programming", and is most often
due to poor encapsulation.
I've read C programs containing goto statements where I would not have
used them, that nonetheless remained quite readable, while I've
encountered several programs where goto is not employed at all, which
remain very cumbersome to read because every snippet of code gets its
hands dirty with the implementation details of several distinct
facilities. This leads me to believe that the taboo against goto
(while of course well-justified) has been grossly overemphasized,
while proper encapsulation, despite the fairly widespread recognition
of its benefits, remains one of the most neglected skills among
lesser-skilled (but professionally employed) programmers.
--
(My 2¢, of course)
Micah J. Cowan
Programmer, musician, typesetting enthusiast, gamer...
http://micah.cowan.name/
I think most people will strive to avoid type-testing elements in
heterogenous collections but when the time comes where you need it most
design-arounds end up being dynamic_cast in disguise. Why not just embrace
it - you know you want to.
Andrew
> Redefine "external" and "subsystem interface" just a little bit and
> everyone's happy. I think traversing a collection and only acting on certain
> types is preferable to having the same object in two collections, just to
> avoid dynamic_cast or, even worse, forcing functionality with do-nothing
> defaults into the superclass (although I didn't hear anyone advocating
> that.)
Add no-op defaults to the parent class!
--
Phlip
http://assert2.rubyforge.org/
--
Ian Collins.
IMHO, you're completely wrong :P
The last experience I had of picking up an untyped language was some C#
written in .Net 1 - without ...err... is it Generics in .Net?
The code was very efficient, but not very readable, and this was not in
the least help by the major data structure being a collection of
objects. Which were, upon examination, each made of objects. Which,
upon further examination, turned out to be strings.
A strongly typed language stops you doing anything so stupid. And I
note that at release 2.0, M$ fixed this particular problem.
But this is like which is better - Car or motorbike? Opinions are bound
to differ.
Andy
Now you've got me curious. What do you print your UML diagrams on?
I've spent over a year trying to get a large-format printer. It's well
over 100 classes BTW, I lost count at 250 today and don't have a tool
that'll do it manually - but less than your 550.
But if our projects take us a couple of years, and yours 6-9 months,
something doesn't add up.
And I can't give too many details of what we're up to, commercial
confidentiality and all that.
Andy
> Matthias Buelow wrote:
>>
>> While I'm not a Python fan, they're correct here (imho).
>
> IMHO, you're completely wrong :P
>
> The last experience I had of picking up an untyped language was some C#
> written in .Net 1 - without ...err... is it Generics in .Net?
>
> The code was very efficient, but not very readable, and this was not in
> the least help by the major data structure being a collection of
> objects. Which were, upon examination, each made of objects. Which,
> upon further examination, turned out to be strings.
>
> A strongly typed language stops you doing anything so stupid. And I
> note that at release 2.0, M$ fixed this particular problem.
From your description, I have a hard time seeing any problem at all. So
there was a collection of collections of collections of strings? What is
stupid about that?
Besides, I can have a set< set< set< string > > > anytime I want in the
strongly typed language C++. Actually, in my programs a data-structure
depth of four or more is not at all rare. Of course, this is usually hidden
by typedefs; but if I see the template instantiation error messages running
down the terminal, I can tell that I am going pretty deep.
> But this is like which is better - Car or motorbike? Opinions are bound
> to differ.
Well, sure. But that does not mean that all opinions are equally well
supported.
Best
Kai-Uwe Bux
In my particular case I am (usually) keeping objects of a certain type
in a container of that type, and I access this container directly when
it's possible to do so (eg. to perform an action on some or all the
objects of that type, as long as this action is independent of any other
objects in the system).
The problem is that not all actions I need to perform can be applied
to objects independently of other objects. In particular, sometimes some
actions need to be performed in a certain order. The two problems are
that the objects in the object-specific container might not be in this
order already, and secondly, even if they were, some actions have to be
performed to all existing objects (regardless of their type) in a strict
order, and cannot be performed on a per-type basis.
Currently I'm maintaining this order by putting base-class-type
pointers in a common container. The order of the pointers in this
container defines the order of all the objects. (Note that there are
also other reasons for doing this. Ordering is just one of them.)
One possibility would be to add a virtual function in the common base
class to perform this specific action. This way all the object types
could implement this action by implementing this virtual function, and
there would be no need for dynamic_cast.
The problem with this is that this base class and some of the objects
derived from it (which are often used here and there in the program) are
in a library. It would mean that I would have to add these new (often
program-specific) features to the library.
This would not only be cumbersome, but I also feel it goes against
proper OO design: You don't go and add application-specific features to
an abstract base class or existing objects in a library. If I did this
for every feature of every program I develop, it would make the library
and the base class less abstract and more bloated.
If dynamic_cast has to be avoided at all costs, some other solution is
needed. Maybe a complete re-design of the library. But even so, I really
haven't come up with a better solution. That's why I'm interested in
hearing new ideas.
> (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.
I'm not exactly sure what this means, and how it is radically
different from using dynamic_cast. (It sounds to me like dynamic_cast
would simply have been replaced with some kind of checking of the 'type'
inside those tuples. If this is so, then it would simply make the whole
implementation more complicated with no real benefit.)
> 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.
If you can use dynamic_cast to reclaim the type of the object, you
really haven't thrown it away. Sometimes (not often, but sometimes)
the best way to reclaim that type is with a dynamic_cast.
--
Robert C. Martin (Uncle Bob) | email: uncl...@objectmentor.com
Object Mentor Inc. | blog: www.butunclebob.com
The Agile Transition Experts | web: www.objectmentor.com
800-338-6716 |
> If dynamic_cast has to be avoided at all costs, some other solution is
> needed.
If dynamic_cast must be avoided at all costs, then virtual functions
must also be avoided; since I can implement dynamic cast with nothing
more elaborate than two virtual function deployments.
dynamic_cast might be ugly syntax; but semantically it is no more ugly
than double dispatch. Indeed it can be implemented with double
dispatch.
And in real life you fix the code you're given and
don't tell your "manager" that you're first going
to spend a few months redesigning the legacy code
to remove the need for ugly workarounds.
Of course, if you're designing new code then the
idea of having to create a template to make dynamic
casting easier should be a warning sign that the
design needs improvment. In older legacy code that
you can't redesign you may as well leave in the ugly
syntax.
--
Darin Johnson
No, dynamic_cast punts. It's a query on object state.
> What's the difference between a member function
> "supportsInterfaceX()" and dynamic_cast< X* >. Except that
> providing the member function means that the base interface must
> know about the extended interface.
Nothing.
> > I don't think it is necessarally a good idea to put type
> > information in the collection, but it is a good idea to only
> > navigate relationships along the trajectory they were designed to
> > be navigated along (i.e., follow the UML arrows, don't try to
> > backtrack against them.) And, I think it is important to follow
> > the "tell, don't ask" principle.
> > (http://pragmaticprogrammer.com/articles/tell-dont-ask) Asking
> > the object its type and then telling the object to do something
> > based on the answer goes against this principle.
>
> The problem with this is that it means that everything any derived
> class supports must be known and declared in the base class.
No, it doesn't.
> And how do you handle the different levels of functionality? What
> happens if you tell an object to do something it's not capable of?
What happens is, you have a design error.
> (Note that the article you site is mainly concerned with not
> exposing mutable state, in order to reduce coupling.
The type of the object is part of the objects state. The word "mutable"
is nowhere to be found in the article. "as the caller, you should not be
making decisions based on the state of the called object that result in
you then changing the state of the object."
The idea is that you shouldn't be telling the object to do X, you
shouldn't be telling it to change its state to X, you should be telling
it about changes in its environment.
I can see it now. I order some RAM for my computer, and the producer
ships me 30 boxes for which I have to check each invoice to see if it is
what I *really* want, shipping the others back to the producer... How
about this instead. I ask for type X and I get type X?
> And you still haven't responded to Juta's question about what
> you do when the transporter is managing several different
> producer/consumer relationships, using different types, and the
> order is significant.
I also haven't answered what a designer should do if all his classes
derive from Object and all his pointers are Object*, forcing him to
dynamic_cast every time he wants to call a member-function. There are
uncountable numbers of designs that would require dynamic_cast. If the
design requires you to use dynamic_cast, then (as I've said before) use
it. But IMHO, the design could have been better.
> And so on. The fact remains that I've found cases where
> dynamic_cast was justified, in the sense that it was more cost
> efficient than the alternatives. (With cost being measured in
> program readability and maintainability, which basically end up
> the equivalent of monitary cost in the end.)
This is simply a difference of experience I guess. I'm certainly not
going to second guess your sense that dyanic_cast is justified, all I
can say is that I have never found a case in OO code where it is.
Print UML? You got to be kidding. :-) The tool I used to count classes
is called "find 'class'". :-)
> But if our projects take us a couple of years, and yours 6-9 months,
> something doesn't add up.
Maybe we have much smaller classes?
> And I can't give too many details of what we're up to, commercial
> confidentiality and all that.
The project with 550 classes (BTW that doesn't include structs :-) was
this one: (http://www.gorilla.com/#hannah)
> On 2008-03-10 18:24:30 -0500, Juha Nieminen <nos...@thanks.invalid> said:
>
>> If dynamic_cast has to be avoided at all costs, some other solution is
>> needed.
>
> If dynamic_cast must be avoided at all costs, then virtual functions
> must also be avoided; since I can implement dynamic cast with nothing
> more elaborate than two virtual function deployments.
>
> dynamic_cast might be ugly syntax; but semantically it is no more ugly
> than double dispatch. Indeed it can be implemented with double
> dispatch.
Right dynamic cast is doubly dispatching. However, "implemented by" /=
"is". (BTW one of the types in dynamic cast is statically known, so it is a
weaker case of double dispatch.)
But the difference is in the interfaces and substitutability. Dynamic cast
is an operation on pointers, while the method is supposedly to work on the
target type. This clutter, as well as exposure of pointers, is what makes
it bad. A consequence of this design is that the dispatch emulated by cast
+ dereference + method call may fail at run time. So the semantics of the
method gets changed. We mean one thing but call another. (I omit the rest
as it falls under "why dynamically-typed is untyped" (:-))
--
Regards,
Dmitry A. Kazakov
http://www.dmitry-kazakov.de
> > 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...
I'm finding this entire thread like that!
Could someone start using a concrete example please!
Why do the objects need to be ordered? (some sort of
ordering was the nearest to an example I've seen).
So will a graphical editor do?
class Shape
{
};
class Square: public Shape
{
};
class Circle: public Shape
{
};
So a heterogeneous collection might be
std::vector<Shape*> picture;
and you'd use a dynamic cast if you needed to know if a shape
was a Square (though why you'd need to know that...).
Is the sugestion that picture holds type info?
--
Nick Keighley
> >> 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...
>
> 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.
with my Shape example (there are some heavy trimmers on this group...)
use seperate Circle and Square collections rather than a heterogeneous
Shape 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.
the Shape collection also holds a field with type info (yuck!)
> 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.
for instance? Could you give a simple concrete example?
> 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.
<snip>
--
Nick Keighley
> > > > 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...
>
> > 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.
>
> Actually, I'm advocating choice 3:
> Have the producer provide the type information directly to the
> consumer, this doesn't necessarally have to be through the collection.
so if the consumer wants Squares give him Squares and only Squares.
If he wants all the Shapes in a picture, in order, then give him that.
Is "order" in the nearly-example an ordering (a <= b <= c ...) and
not a purchase ("I'd like to order 3000 left handed blivets")
> If the collection's job is to keep track of ordering information, then
> that should be its only job. The consumer shouldn't be expected to use
> that collection to *also* get all objects of a particular type.
so if you had two "collections" or streams of objects or whatever
then you'd have two different producers. One "ordered" and one "all
objects"?
> > > > > 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...
> > > 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.
>
> > Actually, I'm advocating choice 3:
> > Have the producer provide the type information directly to the
> > consumer, this doesn't necessarally have to be through the
> > collection.
>
> Isn't this exactlly what dynamic_cast does?
could *you* give a simple concrete example. Why don't you know the
type?
> What's the
> difference between a member function "supportsInterfaceX()" and
> dynamic_cast< X* >. Except that providing the member function
> means that the base interface must know about the extended
> interface.
yes
> > If the collection's job is to keep track of ordering information,
what is "ordering information"? Z order? creation order?
> > then
> > that should be its only job. The consumer shouldn't be expected to use
> > that collection to *also* get all objects of a particular type.
> > I don't think it is necessarally a good idea to put type information
> > in the collection, but it is a good idea to only navigate
> > relationships along the trajectory they were designed to be navigated
> > along (i.e., follow the UML arrows, don't try to backtrack against
> > them.) And, I think it is important to follow the "tell, don't ask"
> > principle. (http://pragmaticprogrammer.com/articles/tell-dont-ask)
> > Asking the object its type and then telling the object to do something
> > based on the answer goes against this principle.
>
> The problem with this is that it means that everything any
> derived class supports must be known and declared in the base
> class.
but why would you want all the objects in certain order
unless you were going to apply a common operation to them?
If they don't all support the common operation why do you want them?
Would the Visitor pattern be any use?
> And how do you handle the different levels of
> functionality?
for example?
> What happens if you tell an object to do
> something it's not capable of?
why would you do that?
> (Note that the article you site
> is mainly concerned with not exposing mutable state, in order to
> reduce coupling. There's nothing "mutable" about the results of
> dynamic_cast. In fact, used correctly, it reduce coupling, by
> keeping not only the state, but the extended interface, out of
> sight except to those who need to know.)
<snip>
--
Nick Keighley
I'm not so sure I would categorically object to no-op defaults. They
are a valid design choice in some situations IMO. Valid in more
situations than down casting for sure.
To Ian's comment, adding no-op defaults requires only that the base
class interface be designed for the contexts it is used in. Which of
course, has to be done in any case.
It's not really that you need dynamic cast to *know* if the shape is a
Square. In this case you need it if you want to *do* something to the
object if it's a Square, and this operation is not supported by Shape,
only by Square.
You could have two vectors:
std::vector<Square*> squares;
std::vector<Circle*> circles;
But then you lose their relative ordering. If you need to perform some
operations to all the shapes in order, you can't do it with only that
information.
You could have both:
std::vector<Shape*> picture;
std::vector<Square*> squares;
std::vector<Circle*> circles;
The pointers in 'picture' point to the same objects and the pointers
in the two other vectors. Now you can both directly access the Squares
and Circles, and you can perform something to all objects in order.
However, that's not good either. Now many operations become
exceedingly complicated. For example, removing the 5th shape in 'picture'.
Another problem is that if you need to perform an operation to all
Squares, but in the order in which they are in 'picture', it also
becomes complicated: Either you need to use dynamic_cast, or you need to
keep 'squares' in the same order as the pointers are in 'picture'. The
latter can be very complicated if the relative order of the shapes is
changed.
> >> >>> Dynamic_cast has its place in good OO design; it just
> >> >>> shouldn't be abused.
> >> >> Just like goto? :-)
> >> > No. Goto never has its place.
> >> Yes it does. I can think of several places where it's really
> >> valuable.
> >> Assembler is impossible without it.
> >And Fortran IV:-).
>
> Not quite. Don't forget Fortran 4's main control structure was
> the "arithmetic if", which could jump backwards ;-/
So you could replace all of the goto's with arithmetic if's in
which all three targets were the same:-).
The classical implementation of a while loop in Fortran-IV did
use a goto, however:
10 IF (condition) 20,30,20
20 C While loop code here...
GOTO 10
30 CONTINUE
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
>>> Add no-op defaults to the parent class!
>>>
>> This requires you know what extensions derived classes will add and what
>> types they will use, or have access to the base to add the no-ops.
>>
> I'll hazard a guess: Phlip was just trying to throw me a bone by asking for
> something everybody is already objecting to.
Sorry - I didn't read the thread.
Going forward, if you can't change that source, you have an "outer problem". Not
an OO problem. Dependency management does not mean "never change this source".
That's a good goal; it just makes the times when you _do_ change the base class
more safe and meaningful.
--
Phlip
> > "Ian Collins" <ian-n...@hotmail.com> wrote in message
> > > Phlip wrote:
> > > > Add no-op defaults to the parent class!
> > > This requires you know what extensions derived classes
> > > will add and what types they will use, or have access to
> > > the base to add the no-ops.
> > I'll hazard a guess: Phlip was just trying to throw me a
> > bone by asking for something everybody is already objecting
> > to.
> I'm not so sure I would categorically object to no-op
> defaults.
I don't think anyone would, although it always depends.
> They are a valid design choice in some situations IMO. Valid
> in more situations than down casting for sure.
The problem is encapsulation. The base class shouldn't
necessarily know about what additional interfaces the derived
class may decide to add. (Sometimes, it's appropriate that it
know it, and other times, it isn't.)
> To Ian's comment, adding no-op defaults requires only that the
> base class interface be designed for the contexts it is used
> in. Which of course, has to be done in any case.
I think you're missing the point. The base class interface is
only designed for a specific use. Some derived classes may want
to define additional functionality. The reason the client code
uses a dynamic_cast is precisely because the base class wasn't
designed to be used in the given context.
Consider a concrete example. Objects are identified by DN's.
You certainly don't want all of the functionality of every
possible object declared down in the basic object type
manipulated by the directory services. All you see there are
the basic functionalities: create, destroy, get, set and action.
Suppose that certain "attributes" are used to set up
relationships; when you receive a set request, the attribute
value is a DN. But of course, in your actual object, you don't
support arbitrary relationships; the relationship depends on the
type of the target object. So in the set request, you ask the
directory service for the object (a pointer to the object, in
fact), and dynamic_cast it to the type you support. If the
dynamc_cast fails, you reject the request with an error; if it
succeeds, you establish the relationship (whatever that means in
the context of the object). Thus, for example, a connection
point or a termination point has an implementedBy relationship
(which in this case will normally be specified as an argument to
the constructor, rather than using a set method, later); the
only way to communicate it is via a DN, so you have to
dynamc_cast, and reject the argument if it isn't the right type
(e.g. if someone requests the creation of a termination point
implemented by an event forwarding discriminator).
As soon as you're dealing with any sort of middleware (even
middleware in the same process), you'll probably need
dynamc_cast at the receiving end---the middleware should not
know the details of what it is connecting. As soon as you have
to deal with multiple versions in the clients or the servers,
you're likely to need dynamic_cast as well---you can avoid it,
but avoiding it more or less comes down to reimplementing what
it does yourself. I suspect that there are other cases as well,
but I've had to deal with those two often enough in practice to
be aware of them. (When we implemented the system I described
above, dynamic_cast didn't exist yet in the language. So we
invented our own version of it. With a lot more work than would
have been necessary with direct support in the language.)
> > > Actually, I'm advocating choice 3:
> > > Have the producer provide the type information directly to the
> > > consumer, this doesn't necessarally have to be through the
> > > collection.
> > Isn't this exactlly what dynamic_cast does?
> No, dynamic_cast punts. It's a query on object state.
Sorry, but I fail to see any difference between having the
producer provide type information, and having the compiler do it
for him. In the end, it boils down to the same thing, except
that 1) having the producer do it explicitly is more work for
the programmer, and 2) the producer can lie.
> Nothing.
> No, it doesn't.
The article didn't mention "mutable", but all of the examples
and the reasoning it applied only applied to mutable.
> The idea is that you shouldn't be telling the object to do X,
> you shouldn't be telling it to change its state to X, you
> should be telling it about changes in its environment.
Agreed. You tell it to do something. In practice, however,
objects may pass through many layers of intermediate software,
which knows nothing about (or should know nothing about) what
services the provider actually offers, and what services the
consumer needs. In practice, however, real software often has
to deal with different versions: you must query whether your
partner supports some new functionality, and be prepared to use
an alternate strategy if it doesn't. Those are, IMHO, two cases
where dynamic_cast is called for---in the first case, it
actually helps design (by encapsulating the functionality at the
relevant level).
> > On Mar 9, 5:09 pm, "Daniel T." <danie...@earthlink.net> wrote:
> > > On Mar 9, 11:38 am, "H. S. Lahman" <h...@pathfindermda.com> wrote:
> > > > Responding to Nieminen...
> > > > > > 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...
> > > > 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.
> > > Actually, I'm advocating choice 3:
> > > Have the producer provide the type information directly to the
> > > consumer, this doesn't necessarally have to be through the
> > > collection.
> > Isn't this exactlly what dynamic_cast does?
> could *you* give a simple concrete example. Why don't you know
> the type?
Because the middleware didn't provide it. Because the
middleware doesn't care about the type---it's just there to
ensure communication.
Because I have no control over what version of the software the
client is running. So I have to be prepared to handle two
different versions.
> > What's the difference between a member function
> > "supportsInterfaceX()" and dynamic_cast< X* >. Except that
> > providing the member function means that the base interface
> > must know about the extended interface.
> yes
> > > If the collection's job is to keep track of ordering
> > > information,
> what is "ordering information"? Z order? creation order?
The order of the requests?
> > > then
> > > that should be its only job. The consumer shouldn't be expected to use
> > > that collection to *also* get all objects of a particular type.
> > > I don't think it is necessarally a good idea to put type information
> > > in the collection, but it is a good idea to only navigate
> > > relationships along the trajectory they were designed to be navigated
> > > along (i.e., follow the UML arrows, don't try to backtrack against
> > > them.) And, I think it is important to follow the "tell, don't ask"
> > > principle. (http://pragmaticprogrammer.com/articles/tell-dont-ask)
> > > Asking the object its type and then telling the object to do something
> > > based on the answer goes against this principle.
> > The problem with this is that it means that everything any
> > derived class supports must be known and declared in the base
> > class.
> but why would you want all the objects in certain order
> unless you were going to apply a common operation to them?
Who knows? But it doesn't seem so unreasonable to me that
different operations may still require ordering between them.
> If they don't all support the common operation why do you want
> them?
> Would the Visitor pattern be any use?
Maybe.
My point is just that there are a number of different tools
available, and no one tool is always the right one.
> > And how do you handle the different levels of
> > functionality?
> for example?
The fact that the middleware doesn't know (and isn't supposed to
know) the services provided by the higher levels.
In large projects, the software tends to be structured very much
like network protocols. The IP layer doesn't know beans about
NNTP, and shouldn't. Similarly, the various layers that connect
the different components of a large application shouldn't know
beans about the services provided by those components.
> That's probably because static typing is not a desirable
> feature in programming in the first place.
That depends on whether you want your program to work reliably
or not. Violate the type system (static or dynamic), and the
code doesn't work. If the language you're using uses static
type checking, you get a compile time error. If it uses dynamic
type checking, you get long debugging sessions at the customer
site.
> It is added complexity, which is (arguably) sometimes
> necessary, more necessary with some languages than others. The
> languages I know that put emphasis on static type correctness
> (for example, ML and Haskell) are extremely unwieldy to
> program in.
I know a couple of Smalltalk experts who migrated to Java, and
had nothing but good things to say about the static type
checking provided by that language. I tend to work on more or
less critical applications, and find that even Java's static
typing is too weak for my tastes. Apparently, I'm not alone,
since since I've used it, the Java authors have felt it
necessary to add templates (improved static type checking).
> No fun at all. Nobody I think, not even here, would say, "oh,
> I miss static typing. It was so neat." It's a burden on the
> programmer best avoided if one can.
It's a burden on the programmer, yes. It means that he actually
has to design what he writes, and document it up front, in a
standard way that even a compiler can understand.