dynamic_cast is ugly!

A

Andy Champ

How many times have you written

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

and wondered WTH you have to quote the type twice?

So I built this:

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

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


.... which lets you type

someType* var = AutoCast(someOtherPtr);

Surely it's been done before? If not:

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

Andy
 
M

Micah Cowan

Andy Champ said:

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.
 
G

Greg Herlihy

How many times have you written

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

and wondered WTH you have to quote the type twice?

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
 
A

Andy Champ

Micah said:
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.

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

Daniel T.

Andy Champ said:

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.
 
J

Juha Nieminen

Micah said:
Because it pretty much always indicates a design flaw. Polymorphism is
the preferred idiom.

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.
 
J

Juha Nieminen

Greg said:
The C++ cast operators, including dynamic_cast<>, were deliberately
designed to have an unwieldy syntax precisely to discourage C++
programmers from using them.

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).
 
M

Michael DOUBEZ

Juha Nieminen a écrit :
Do you have any reference to corroborate this?

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
 
D

Daniel T.

Juha Nieminen said:
Micah said:
Because it pretty much always indicates a design flaw.
Polymorphism is the preferred idiom.

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 [snipped example of poor
design containing a "manager".]

The other alternative is to use [snipped example of the Acyclic
Visitor pattern (http://www.objectmentor.com/resources/articles/acv.pdf)
to get around the poor design.]

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

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.
 
J

Juha Nieminen

Daniel said:
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.
 
D

Daniel T.

Daniel T. wrote:

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.
 
J

Jeff Schwab

Michael said:
Juha Nieminen a écrit :

I guess it is one of those urban legend.

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."

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.
 
G

Greg Herlihy

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.

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
 
I

Ian Collins

Daniel said:
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.

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

Daniel T.

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.

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.
 
I

Ian Collins

Daniel said:
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.
 
D

Daniel T.

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.

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.
 
P

Paul Brettschneider

Jeff said:
[...]
<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
 

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,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top