C++ RTTI and derived classes

S

Shaun

My C++ is a bit rusty. Here's what I'm attempting to do:

class Cmd { };
class CmdA : public Cmd { };
class CmdB : public Cmd { };
....
Cmd *a = new CmdA ();
Cmd *b = new CmdB ();

First problem:
cout << typeid (a).name ()
cout << typeid (b).name ()
both return Cmd * types. My desired result is CmdA* and CmdB*. Any
way of accomplishing this other than:
if (dynamic_cast <CmdA *> (a)) ...

Second, I would like to do something like this:
class Target {
public:
void handleCommand (Cmd *c) { cout << "generic
command..." }
void handleCommand (CmdA *a) { cout << "Cmd A"; }
void handleCommand (CmdB *b) { cout << "Cmd B"; }
};

Target t;
t.handleCommand (a);
t.handleCommand (b);

and get the output "Cmd A" and "Cmd B". Right now it prints out
"generic command..." twice.

Thanks

-Shaun
 
S

Shaun

First problem:
cout << typeid (a).name ()
cout << typeid (b).name ()
both return Cmd * types.  My desired result is CmdA* and CmdB*.  Any

cout << typeid (*a).name ();
cout << typeid (*b).name ();


And I think the second problem is called multiple dispatch and can be
handled with the visitor pattern
 
A

Alf P. Steinbach

* Shaun:
My C++ is a bit rusty. Here's what I'm attempting to do:

class Cmd { };
class CmdA : public Cmd { };
class CmdB : public Cmd { };
...
Cmd *a = new CmdA ();
Cmd *b = new CmdB ();

First problem:
cout << typeid (a).name ()
cout << typeid (b).name ()
both return Cmd * types.
My desired result is CmdA* and CmdB*. Any
way of accomplishing this other than:
if (dynamic_cast <CmdA *> (a)) ...

The dynamic_cast wouldn't work either, but it's more on the right track.

For the dynamic_cast to work the statically known type Cmd needs to have at
least one virtual member routine, e.g. a virtual destructor suffices, and we
then say that Cmd is a "polymorphic type".

For the typeid expressions you'd need to check the typeid's of the referenced
objects, e.g. typeid(*a). Note, in passing (it's always fun to consider such
counter-intuitive details of the C++ language specification) that typeid is the
only place in C++ where you can dereference a nullpointer without Undefined
Behavior. That's because it's not really a function call but an operator that
the compiler translates to magic. :)

Second, I would like to do something like this:
class Target {
public:
void handleCommand (Cmd *c) { cout << "generic
command..." }
void handleCommand (CmdA *a) { cout << "Cmd A"; }
void handleCommand (CmdB *b) { cout << "Cmd B"; }
};

Target t;
t.handleCommand (a);
t.handleCommand (b);

and get the output "Cmd A" and "Cmd B". Right now it prints out
"generic command..." twice.

Yeah, what you're looking for is called the "visitor pattern".

There are umpteen ways to do it, differing in what types know about what. If you
can assume that all Cmd derived types are known up front at the place of
definition of Target, then you can do it /without any casting/. It's very nice,
but, repeat, the cast-free version requires knowledge of all derived classes:

// Disclaimer: off-the-cuff code.

class Cmd;
class CmdA;
class CmdB;

class Target;

class Target
{
public:
void handle( Cmd const& ) { cout << "generic command..."; }
void handle( CmdA const& ) { cout << "Cmd A"; }
void handle( CmdB const& ) { cout << "Cmd B"; }
};

class Cmd
{
public:
virtual ~Cmd() {}
virtual void callHandlerOn( Target& t )
{
t.handle( *this );
}
};

class CmdA: public Cmd
{
public:
virtual void callHandlerOn( Target& t )
{
t.handle( *this );
}
};

class CmdB: public Cmd
{
public:
virtual void callHandlerOn( Target& t )
{
t.handle( *this );
}
};

The apparently identical repeated code in CmdA and CmdB isn't really identical,
because the statically known type of '*this* differs... :)

And the general idea here is that, regardless of programming language, a virtual
call of a method does a downcast for you (namely of the 'this' pointer in C++),
in a type safe manner.

If you can't assume that all derived classes are known at the place of
definition of Target, then you need some explicit cast, like dynamic_cast, and
the main idea is then to centralize that in the method I've called
'callHandlerOn' above. Any command object then knows its own type, and tries to
downcast the Target argument to an interface that supports that self-type, and
if there's no such interface, using more general Target functionality.


Cheers & hth.,

- Alf
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,818
Latest member
Brigette36

Latest Threads

Top