dynamic_cast is ugly!

K

kwikius

Uh, actually you shouldn't do that. Most re-use should move out to
delegates, not up to base classes.

Please Do enlighten us all with an example ...

regards
Andy Little
 
J

Jerry Coffin

Jerry said:
struct UML_object {
int x_, y_;
virtual void draw(surface_t &) = 0;
};

class UML_interface : public UML_object {
static color_t color;
public:
static void highlight(bool state=true) {
static color_t colors[] = {
RGB(0,0,0),
RGB(255, 0,0)
};

color = colors[state];
// code to force re-draw goes here.
}

square(int x, int y) : x_(x), y_(y) {}

virtual void draw(surface_t &s) {
// draw "interface" on specified surface
}
};

class UML_class : public UML_object {
public:
UML_class(int x, int y) : x_(x), y_(y) {}

virtual void draw(surface_t &s) {
// draw "class" on specified surface
}
};

I don't really understand what you are doing there. It's not even
valid C++.

Sorry -- I tested the code with the classes named 'circle' and 'square',
and when I edited them for posting, I missed changing the 'square' ctor
to 'UML_interface'. My apologies for that.

Here's the code, edited in my normal code editor, with no further
editing after compiling, to ensure against my fat-fingering (which means
it stil includes working drawing code and such, though that's neither
portable nor really topical):

struct UML_object {
virtual void draw(CDC *) = 0;

// Note that this does NOT include highlight().
};

class UML_interface : public UML_object {
int x_, y_, cx_, cy_;
static DWORD color;
public:

// This implements highlight() ONLY for UML_interface
static void highlight(bool state=true) {
static DWORD colors[] = {
RGB(0,0,0),
RGB(255, 0,0)
};

color = colors[state];
}

UML_interface(int x, int y, int cx, int cy) :
x_(x), y_(y), cx_(cx), cy_(cy)
{}

virtual void draw(CDC *pDC) {
CPen pen;
pen.CreatePen(0, 0, color);

pDC->SelectObject(&pen);
pDC->Rectangle(x_, y_, x_+cx_, y_+cy_);
}
};

class UML_class : public UML_object {
int x_, y_, r_;
DWORD color;
public:

// UML_class doesn't include highlight() either.
//

UML_class(int x, int y, int r) : x_(x), y_(y), r_(r),
color(RGB(0,0,0))
{}

virtual void draw(CDC *pDC) {
CPen pen(0, 0, color);
pDC->SelectObject(pen);
pDC->Ellipse(x_-r_, y_-r_, x_+r_, y_+r_);
}
};
(And public member variables?)

Public member variables have a lot worse reputation than they deserve --
though in many cases, they should be of types that ensure against
misuse. As you can see above, they were originally private, but this
increases the amount of code, without contributing anything relevant to
the question at hand.
Maybe one of us is missing the point here?

Yes, you are -- or at least you were. Knowing how Usenet (or the human
psyche) works, you probably figured out that highlight() was specific to
UML_interface about 10 seconds _after_ your post hit the server. Been
there, done that!
The problem is not how to implement a feature which is common to all
the objects. The problem is how to implement a feature which is not.

Of course if a feature is common to all the objects (such as your
highlighting above), you simply make that feature part of the base
class. The problem is that not all features are common to all objects
and cannot be logically placed in the base class.

What you've probably already realized is that highlight() really IS
specific to UML_interface, not common to all the objects.

Of course, you may instead have decided I'm such an idiot that you've
plonked me, so you won't see this either... :)
 
J

Jerry Coffin

All I understood from his post was: If you have a feature which is
common to *all* the objects, put that feature in the base class.

Perhaps I was unnecessarily kind in my previous post. Contrary to the
common situation, you _don't_ seem to have understood the post 10
seconds after you replied. Oh well, chances are pretty good that have by
now in any case...
Well, duh. Hello? That isn't the issue here at all. (*Of course* I do
that every time I can. You don't have to tell me that.)

The issue is that some derived classes might have features which do
not belong to the base class at all. (And, in some cases, couldn't even
be added there.)

Which is, of course, exactly what I demonstrated how to do.

Now, don't get me wrong: I'm not claiming that this particular solution
is _always_ suitable. It just happens that this particular problem was
one where dynamic_cast was being used for little more than forcing a
class of objects to act as if they shared state, and really sharing
state was a much more straightforward solution.
 
C

coal

There's a definite advantage in that you state up front which
objects are transportable. In many cases, such objects may have
to fulfill additional requirements as well. Deriving from a
common base allows some additional compile time checking.

What I suggest states up front what your intentions are as well.
On the other hand, the slln.net site says, "Serialization support can
often be added to types without modifying those types, or them even
being aware they are playing along. For example ...

Out of the box it supports all STL containers (and workalikes), nested
arbitrarily deep. By extension, we can (rightfully) assume that...
Clients can easily extend it to support their own types using, often
non-intrusively."

I'm closer to you in terms of approach than I am to the s11n author,
but I don't know of any C++ serialization framework that takes the
approach you suggest. S11n doesn't. Boost doesn't. RCF doesn't.
What I advocate doesn't.
As for the performance hit, be serious. You're going through an
extra layer; that will cost something in terms of runtime. More
than any virtual function call, anyway.

Doesn't what you propose also use an extra layer? I thought
it was all other things being equal.
Which can work too, but is less sure than if the programmer
explicitly states that he implements the TransportableObject
contract. And at least in C++, doing this way means using
templates, with the resulting increase in coupling, code bloat
and explosion of compile times. (This would be partially
mitigated if your compiler supports export, but the code bloat
and some additional coupling is inevitable.)

I don't guess you are advocating writing all marshalling code
by hand, but if you aren't using code generation/templates, I'm not
sure what choice you have. All the serialization libraries use some
form of code generation. Those that rely heavily on templates usually
are slow build-wise and produce bloated executables.
http://www.webebenezer.net/comparison.html.
Inhertance remains the simplest and most efficient means of
specifying conformance to a contract. It does get in the way of
the programmer accidentally creating a class which conforms to
the superficial aspects (presence of such and such a function),
but being unaware of the actual contract, and not implementing
the desired semantics, yes.

That would result in a compile-time error in what I advocate.
If functions aren't implemented (generated) and calls are made to
those functions, the compiler will complain.

Brian Wood
 
J

Juha Nieminen

Which is why I wouldn't generally put those Derived in a collection of
Base *, but in a collection of Derived *.

Maybe you haven't read the problem which has been posed several times
now? I'm getting tired of repeating it.

You may have several types derived from the base class, and they are
all stored in a common data container, and their *order* inside that
data container is crucial. Storing each type in their own type-specific
data container doesn't work because this ordering is lost.

Some operations need to be performed to the objects in the order in
which they are stored.

The majority of such operations can be performed by having a virtual
function in the base class, so there's no problem. However, there may be
some operations which cannot be put in the base class (or even
operations which cannot be performed by the objects at all, eg. because
they don't have access to something needed).
 
N

Nick Keighley

Daniel T. wrote:

Juha, you snip rather generously!

All I understood from his post was: If you have a feature which is
common to *all* the objects, put that feature in the base class.

expect that ean't the issue. It was if a feature is common to all su-
objects
of a particular sub-type- then make it a static property. Sounds quite
cool
to me

Well, duh. Hello? That isn't the issue here at all. (*Of course* I do
that every time I can. You don't have to tell me that.)

The issue is that some derived classes might have features which do
not belong to the base class at all. (And, in some cases, couldn't even
be added there.)

Read The Code



this is the "excellent" post
***
the request
should be dealt with much more abstractly -- something more like
"highlight interfaces", using a relatively abstract description of
the
desired action instead of directly describing its physical
manifestation.

Although I can't say I've seen a use for this specifically in UML,
let's
assume for the moment that it really was something you wanted. In
that
case, I think stepping through the collection of all the objects and
setting the interfaces to highlighted is only a minor improvement
over
stepping through them and setting rectangles to yellow.

Instead, if we want to be able to highlight all the interfaces (or
whatever) we'd share the "highlighted" vs. "normal" state (or perhaps
the current color) among all objects of that type:


struct UML_object {
int x_, y_;
virtual void draw(surface_t &) = 0;



};


class UML_interface : public UML_object {
static color_t color;
public:
static void highlight(bool state=true) {
static color_t colors[] = {
RGB(0,0,0),
RGB(255, 0,0)
};

color = colors[state];
// code to force re-draw goes here.
}


square(int x, int y) : x_(x), y_(y) {}


virtual void draw(surface_t &s) {
// draw "interface" on specified surface
}



};


class UML_class : public UML_object {
public:
UML_class(int x, int y) : x_(x), y_(y) {}

virtual void draw(surface_t &s) {
// draw "class" on specified surface
}



};


color_t UML_interface::color;

***
 
N

Nick Keighley

As I noted elsethread, I'd start from the fact that as we're postulating
the design, we're using a shared action among all objects of a given
type to simulate sharing state among all objects of a given type.

If we want objects to act like they're sharing state, I think we should
express that directly rather than simulating it by sharing an action
across those objects.

yes, good point
My earlier post contained an implementation that
answered the question that was asked, but wasn't (IMO) particularly
extensible.

I think we had some posts cross. I hadn't seen you post until today.

Realistically, we know that if you want to be able to highlight objects
of one type, you probably also want to be able to highlight objects of
other types as well -- in fact, you _probably_ want to be able to
highlight objects of _any_ type.
yes

The approach I used elsethread _could_ do that, but would require code
duplicated across all the descendants of 'shape' to do so (to create and
manipulate a static variable in each). Realistically, we also know that
there's more to the UI of an object than just its current color. That
implies that there would be quite a lot of code duplicated across the
hierarchy, something we'd generally much rather avoid.

To accomplish this, we probably want to start by creating a UI class
that describes the current UI state of an object. We create an instance
of this UI class for each class of object in our hierarchy. When we
create each object in the hierarchy, we include a pointer (or reference)
to the appropriate UI state object that's shared among all objects of
that type. When we want to (for example) highlight objects of a
specified type, we do NOT manipulate the individual objects of that type
-- rather, we manipulate the UI state object shared by all objects of
that type:

oh yes, much better

class UI_state {
        enum { NORMAL, HIGHLIGHTED } highlight_state;
        color colors[2];
        int x_, y_;
        /* other UI state here */

public:
        highlight() { highlight_state = HIGHLIGHTED; }
        normal() { highlight_state = NORMAL; }

        color current_color() const { return colors[highlight_state]; }

};

enum UML_objects {
        UML_CLASS,
        UML_INTERFACE,
        UML_COMPONENT,
        /* ... */
        UML_OBJECT_LAST

};

std::vector<UI_state> UI_states(UML_object_last);

class UML_object {
        UI_state &ui_;
public:
        virtual void draw(surface_t &surface) const = 0;

}

class UML_class : public UML_object {
public:
        UML_class(int x, int y)
                : x_(x), y_(y), ui_(UI_states[UML_CLASS])
        {}
        virtual void draw(surface_t &surface) const {
                // draw self onto surface using ui
        }

};

class UML_interface : public UML_object {
public:
        UML_interface(int x, int y)
                : x_(x), y_(y), ui(UI_states[UML_INTERFACE])
        {}

        virtual void draw(surface_t &surface) const {
                // draw self onto surface using ui
        }

};

Now, to highlight all interfaces, we do NOT look through our collection
of objects looking for interface objects, and then manipulate the color
of each. Instead, 'UI_states[UML_INTERFACE].highlight()' does the whole
job at once. Highlighting objects of a different type obviously requires
nothing more than choosing the appropriate constant. In any case, we're
manipulating a single shared state instead of searching for a set of
objects with duplicated state.

I should add that I doubt this design would really apply to a UML
editor. I can't remember ever having wanted to highlight all objects of
a given type.

I was having trouble in all the theory talk (hetrogenous collections)
and wanted something more concrete. Squares and Circles *still* seemed
a bit unreal. So I thought a noddy UML editor might be better.
The trouble with noddy examples is they remain noddy.

Rather, highlighting would typically be based on a
relationship, such as "everything that inherits from X", or "everything
that implements Y", or "everything that depends on Z". As such, you're
probably going to determine the highlight state based on the
relationships defined in the model, not simply the type of an object.

As such, I think the design is probably for something nobody has ever
(and probably will ever) want, at least as presented. OTOH, I can
imagine situations where such a thing really would be useful, and for
such a situation, I think this is a much cleaner implementation than
anything using dynamic_cast.

IMO, this design is much more adaptable to a real-world scenario. If I
was designing a UML editor, I don't think I'd use a static class
hierarchy to represent the UML elements. Rather, I'd put almost
everything related to each UML element into initalization files of some
sort, and simply load those up during program startup. For example, an
implementation on Window would load a series of metafiles, and when it
needed to draw an object, it would use PlayMetafile or PlayEnhMetafile
to do the job.

Using this design, all the UML objects might easily be of precisely the
same type. Sharing the UI state would remain easy, because when we
create an object we know what kind of object we're creating, and we only
need to use a suitable value to relate that object to the appropriate UI
state. In this situation, dynamic_cast wouldn't work at all -- it would
do nothing to distinguish between one UML element and another, since
from the viewpoint of C++ they would all just be objects of a single
type.

thanks very helpful
 
D

Daniel T.

[warning, libral snippage]
FWIW Using dynamic_cast is fine in my book.

dynamic polymorphism is a theory beloved of academics.

outside the ideal world of academia and textbooks, dynamic_cast is
required to discriminate.

It might be approprate to remember what started this thread in the
first place. Andy Champ found the dynamic_cast syntax to be ugly and
asked how often people use it. I said that I have never used it in 10
years of writing professional code.

Now here you are saying that it is required? All I can say is that our
experiences differ.
 
M

Matthias Buelow

Daniel said:
I am a video game programmer, so most of my
work is in the simulation domain... often on systems that are much
like what embedded programmers have to deal with.
OO programming's roots are in simulation work so I sometimes think
that I'm more steeped in the paradigm than many others.

Hmm, that's interesting. Does modelling a highly dynamic environment
such as a game world work well with a rigid object system such as C++'?
I could imagine that such applications in particular would benefit from
a more dynamic system and a more communicative rather than
type-hierarchical approach. Kindof like an agent world vs. a static
object tree.
 
P

Phlip

Matthias said:
Hmm, that's interesting. Does modeling a highly dynamic environment
such as a game world work well with a rigid object system such as C++'?

No. This is why some games ship with the upper 2/3rds of their code in Lua.
Kindof like an agent world vs. a static
object tree.

Right. "Alternate hard and soft layers".
 
A

Andy Champ

Daniel said:
It might be approprate to remember what started this thread in the
first place. Andy Champ found the dynamic_cast syntax to be ugly and
asked how often people use it. I said that I have never used it in 10
years of writing professional code.

Now here you are saying that it is required? All I can say is that our
experiences differ.

Well actually I was providing a way to make it less ugly - but obviously
that's of no use to you in your style of working; others may be able to
use it.
 
J

Jeff Schwab

Phlip said:
No. This is why some games ship with the upper 2/3rds of their code in Lua.


Right. "Alternate hard and soft layers".

That's interesting, and not what I would have expected. I do a lot of
hardware simulation that I take to be very similar to video game
programming, and have better success with C++ as the language for almost
all code that gets shipped. I find dynamic languages useful for testing
and managing the in-house code-base. I looked into Lua at one point,
but didn't pursue it; is it worth checking out? Have you tried
embedding Python or Ruby in your application, or do they just look like
they would be prohibitively heavy-weight?
 
K

kwikius

[warning, libral snippage]
FWIW  Using dynamic_cast is fine in my book.
dynamic polymorphism is a theory beloved of academics.
outside the ideal world of academia and textbooks, dynamic_cast is
required to discriminate.

It might be approprate to remember what started this thread in the
first place. Andy Champ found the dynamic_cast syntax to be ugly and
asked how often people use it. I said that I have never used it in 10
years of writing professional code.

Now here you are saying that it is required? All I can say is that our
experiences differ.

I use currently extensively in a so called DOM lib. Or call it a
directory tree or "duck typing" or whatever.

Firstly I use it for discriminating branches and leaves. If a
dynamic_cast to a branch from a node to a branch fails ( for say
looking up a path), then it throws an exception. Second I use it to
retrieve data from leaves.

In this type of tree you can put any type of data you like in any leaf
node at runtime. You can use various methods to structure the tree.
The simplest is just knowing what type of data lives at
"root.my_stuff.stuff". Data Access is via a function templated on the
type of data. Again if a dynamic_cast to the expected data type fails
it throws an exception.

Its a very versatile and useful library. You can prototype very
quickly with it.

regards
Andy Little
 
K

kwikius

Juha Nieminen a écrit :



I guess it is one of those urban legend.

It is IIRC specifically stated in either The C++ programming language
book, or in the Design and Evolution of C++ book, (both by Bjarne
Stroustruup) I can't remember which, probably TCPPL .

regards
Andy Little
 
K

kwikius

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?

You generally read 'em in from a stream at runtime.

regards
Andy Little
 
J

Jeff Schwab

kwikius said:
It is IIRC specifically stated in either The C++ programming language
book, or in the Design and Evolution of C++ book, (both by Bjarne
Stroustruup) I can't remember which, probably TCPPL .

It's in D&E, and in Bjarne's FAQ. He has also addressed it in the past
in c.l.c++.
 
D

Daniel T.

Matthias Buelow said:
Hmm, that's interesting. Does modelling a highly dynamic environment
such as a game world work well with a rigid object system such as C++'?

Absolutely. IMHO, the only real difference between them is how much of
the design is exposed code.
 
D

Daniel T.

Juha Nieminen said:
Daniel T. wrote:

You want me to add a virtual function named "changeSquareColor" to
the Shape base class if I want to be able to change the color of
squares?

Not at all, that would be a silly name.
That breaks object-oriented design quite horribly. For one, it
breaks the "is-a" relationship. More precisely, it breaks the
"behaves like" property of an "is-a" relationship: If you specify
that "Shape" supports the functionality "changeSquareColor", you
are effectively saying that *all* classes derived from Shape
support that functionality. However, only one does, the others
don't.

Is that really the case? Let's ignore the silly name for a bit and ask
something more important... What is the post-condition of this function?
I would expect the post-condition to be simply "true", i.e., the
function guarantees to return, but nothing else. Since no behavior is
prescribed, I don't see how any "behaves like" property could be broken.

So, why would a client call this function on the shapes in question? To
let them know of a particular event, *not* in order to tell them what to
do. When designing an OO system, it is better to decentralize your
thinking, try to treat each object has a semi-autonomous,
anthropomorphic entity, instead of a billiard ball you are banging
around. The is the basic design behind the observer pattern, notify
others of your state changes rather than telling them what state to
change to.

Is this particular design the best solution for this particular problem?
Probably not, but it does suit other problems where dynamic_cast is used.
The base class would be cluttered with functions specific to some
derived classes.

Absolutely not. I don't recommend putting functions in the base class
that relate to derived classes. I recommend putting functions in the
base class (interface) that relate to client classes.
And besides, in some cases you *can't* modify the base class (eg.
because it's in a library).

As I said more than once, if you are forced by the current design to use
dynamic_cast, then by all means use it.
 
D

Daniel T.

Juha Nieminen said:
You may have several types derived from the base class, and they are
all stored in a common data container, and their *order* inside that
data container is crucial. Storing each type in their own type-specific
data container doesn't work because this ordering is lost.

Some operations need to be performed to the objects in the order in
which they are stored.

Like what?
 

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,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top