Re: dynamic_cast is ugly!
On 15 mar, 18:14, Jeff Schwab <j...@schwabcenter.com> wrote:
James Kanze wrote:
They have to be: A general-purpose transport layer does not
know about the higher layer's object types.
And a specialize transport layer only knows a little. It
doesn't take much specialization to ensure that all of the
objects transported have a common base (i.e.
TransportableObject?), and are polymorphic.
The transport mechanism is just sending bits over a wire,
Maybe, maybe not. There's not necessarily a wire; it's sending
objects between to components. There are many different types
of transport layer. (And even if there is a wire, many
protocols will have all transportable objects derive from some
common base type, or a small set of common base types.)
or at most doing some kind of byte-order or line-ending
conversion.
I think you're confusing the transport layer of OSI network
protocols (TCP, etc.) with the transport layer of OO design.
I have been using the terms somewhat interchangeably: not specifically
meaning OSI layer 4, but "the next layer down" from whichever layer is
being discussed.
OK. That's the sense I'm using as well. (I probably should
have chosen a different term, since transport layer does suggest
the OSI layer 4, and the lower layer isn't always involved with
"transport", in the strictest sense---just hooking components
together somehow.)
This seemed to be enough for the current discussion, but by
all means let me know if there is a relevant difference that
I'm missing. I'm always (well, usually) happy to learn. :)
The OSI layer 4 is a special case of the general case.
For dynamic_cast to be relevant, the transport mechanism has
to know about some base type used by the higher level. The
only case in which I could see this happening is if the
transport layer provides a base type with virtual methods,
maybe "serializable_object" or something, from which
higher-layer objects are supposed to derive. IMO, that kind
of design is completely upside-down and unnecessarily
invasive.
Why? If you want an object to be transportable, you say so. It
seems to me to be part of the basic principles of static type
checking. You're transporting message objects between higher
level components which have connected to the transport layer.
You don't (and can't) transport just anything.
The "message" objects you're describing are just utilities.
It is not, IMO, reasonable for a C++ library to dictate what
classes must be derived from by a higher-layer client.
It's not reasonable for it not to. A C++ library must specify
very clearly which classes are designed to be used as base
classes, and in what ways. By default, the assumption is (or
should be) that you cannot reasonable derive from a library
class. The exceptions should be clearly documented.
The principle applies as strongly to application-specific
libraries as to general-purpose ones. Once you do this, you
are effectively implementing a framework rather than a
library.
In an application, the lower levels are a framework. Or part of
one.
Especially if multiple such pseudo-libraries are to co-exist
in the same application, there is, I think, a real danger that
the client objects will be forced to implement an unwieldy
number of different, sometimes conflicting, interfaces.
If the interfaces conflict, then you do have a problem. I've
not found this to be a problem in practice, however.
A better solution is for "message" to be a concrete type
provided by the transport layer, such that each message is a
configurable object tasked with carrying an arbitrary bundle
of data. It is none of the transport layer's business what is
or is not being sent between two higher-layer objects. This
is one of the few valid uses of void*.
Which is, IMHO, even less type-safe than using a common base
class. And more or less requires the client code to implement
the equivalent of dynamic_cast itself: a lot of extra work, and
additional potential for errors.
There should also be a way for the message to include meta-data about
its payload, such that type information may be retrieved by the message
recipient. A dynamic_cast may be a convenient way of achieving some of
this functionality (if void* is replaced with e.g. transportable*), but
it is not a general solution; seeing dynamic_cast used to retrieve
run-time type information after transport would set off alarms in my
mind, and would seem a good reason to scrutinize both the transport and
application layers' design.
dynamic_cast is never a general solution. I don't think a
"general solution" exists for this problem. dynamic_cast is
part of an appropriate solution in some specific cases. In
others, of course, you'll use other tools.
There is, of course, the Java-style design option to have run-time
tagging interfaces of the java.io.Serializable variety.
Java's serialization is broken. I've never found a case where
it was an appropriate solution. Probably because it attempts to
do too much.
But I'm not sure it's relevant here. The "framework" or the
"transport layer" we're talking about doesn't necessarily
involve serialization.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France, +33 (0)1 30 23 00 34