Which derived class is it?

B

Brad Marts

I would like to have a function that takes as an argument a base class but
performs differently depending on which type of derived class is passed.
Can I tell which derived class is passed?

For example:

class Base
{ //...
};

class D1: public Base
{ //...
};

class D2: public Base
{ //...
};

void f(Base b)
{
// Do one thing if b is of type D1, Do another if b is of type D2
}

Is this the right idea? Or do I have to write multiple copies of f()
with arguments overloaded with the derived classes?

Brad
 
J

jeffc

Brad Marts said:
I would like to have a function that takes as an argument a base class but
performs differently depending on which type of derived class is passed.
Can I tell which derived class is passed?

For example:

class Base
{ //...
};

class D1: public Base
{ //...
};

class D2: public Base
{ //...
};

void f(Base b)
{
// Do one thing if b is of type D1, Do another if b is of type D2
}

Is this the right idea? Or do I have to write multiple copies of f()
with arguments overloaded with the derived classes?

I would certainly not write multiple copies. You might want to read up more
on examples of polymorphism. The power of this is that you don't have to
write multiple copies of functions. On the other hand, you normally don't
want to have to check what the subtype is, either. Normally what you want
to do is achieve "performing differently" by calling functions on those
objects that get passed in. In other words, you bury the different
performance within the subclasses themselves. For example, in your f()
function, what do you want to do? Let's say it's to display something about
the object. So you write a function called "display" and put it in Base and
make it virtual. Then in D1 and D2 you define those functions again,
separately, but you make them do 2 different things. For example,

void D1::display()
{
cout << "This is an object of type D1!";
}

void D2::display()
{
cout << "This is an object of type D2!";
}

In your f function, you merely write
b.display(), and then the correct thing will be displayed, depending on the
type, without you having to know or care in the code. There's a catch here
though - you can't do it if you pass the Base parameter by value like that.
You have to pass it by reference (Base& b), or pionter (Base* pB).
 
D

David Harmon

I would like to have a function that takes as an argument a base class but
performs differently depending on which type of derived class is passed.

This issue is covered in Marshall Cline's C++ FAQ. See the topic
"[20] Inheritance — virtual functions". It is always good to check the
FAQ before posting. You can get the FAQ at:
http://www.parashift.com/cpp-faq-lite/
 
D

Deming He

Brad Marts said:
I would like to have a function that takes as an argument a base class but
performs differently depending on which type of derived class is passed.
Can I tell which derived class is passed?

For example:

class Base
{ //...
};

class D1: public Base
{ //...
};

class D2: public Base
{ //...
};

void f(Base b)
{
// Do one thing if b is of type D1, Do another if b is of type D2
}

Is this the right idea? Or do I have to write multiple copies of f()
with arguments overloaded with the derived classes?

Brad

The idea is all right. However the implementation is not quite right.

Your f function could be altered like this:

void f(Base& b)
{
if(typeid(b) == typeid(D1))/*Do one thing here*/

else if(typeid(b) == typeid(D2))/*Do another here*/
}

Basically used RTTI above.
 
J

Jeff Schwab

Brad said:
I would like to have a function that takes as an argument a base class but
performs differently depending on which type of derived class is passed.

IMNSHO:

The worst of all possible worlds is to test the argument's run-time type
with a switch statement, if-else block, or the like (the "switch on
type" approach).

The next worst of all possible worlds is to code a different version of
the function for each derived type that may be an argument to it (the C
approach).

The usual solution is to give the base class a virtual method, giving
each derived class the option of overriding the method:

struct Base { virtual void behave( ) { /* do one thing */ } };
struct Derived1 { void behave( ) { /* do another */ } };
struct Derived2 { void behave( ) { /* do yet another */ } };

void f( Base& b ) { b.behave( ); }

My favorite solution is the generic approach, which allows you to avoid
the performance and space overhead that accompany the use of virtual
functions:

struct Base { void behave( ) { /* do one thing */ } };
struct Derived1 { void behave( ) { /* do another */ } };
struct Derived2 { void behave( ) { /* do yet another */ } };

template< typename T >
void f( T& t ) { t.behave( ); }

This causes the compiler to implement the indirection at compile time,
instead of run time. The biggest barrier I've had to using this
approach is the pile of existing code (written by yours truly) that
relies on run-time indirection.

Hth,
Jeff
 
S

stelios xanthakis

Jeff Schwab said:
IMNSHO:

The worst of all possible worlds is to test the argument's run-time type
with a switch statement, if-else block, or the like (the "switch on
type" approach).

OT:
And that's why typeID was maybe a not-so-good idea. It can be malused.
The next worst of all possible worlds is to code a different version of
the function for each derived type that may be an argument to it (the C
approach).

C approach would be that each object will have a member my_type,
which will be used in a switch/if. non-OOP. callbacks would be
the other solution, but that's why C++ is here for.
The usual solution is to give the base class a virtual method, giving
each derived class the option of overriding the method:

struct Base { virtual void behave( ) { /* do one thing */ } };
struct Derived1 { void behave( ) { /* do another */ } };
struct Derived2 { void behave( ) { /* do yet another */ } };

void f( Base& b ) { b.behave( ); }

You forgot inheritance?

struct Derrived1 : Base { ... };

Of course.
My favorite solution is the generic approach, which allows you to avoid
the performance and space overhead that accompany the use of virtual
functions:

struct Base { void behave( ) { /* do one thing */ } };
struct Derived1 { void behave( ) { /* do another */ } };
struct Derived2 { void behave( ) { /* do yet another */ } };

template< typename T >
void f( T& t ) { t.behave( ); }

Here on the other hand, we don't need inheritance indeed.
'Derrived1' is not really Derrived but just named so.

Just some more info for the OP:

If -the polymorphic- function "f" has more code:

void f (T &t)
{
...lots of code...
t.behave();
...lots of code...
}

The extra code will be duplicated with each specialization
and thus is not suitable for "embedded systems" where available
memory is 640kb.

Moreover, we are studying a simple case with just one virtual
function. If f() calls two virtuals, "behave1" and "behave2"
then OOP is definitelly superior.


cheers,

stelios
 
P

Patrick Kowalzick

Hi Jeff,
The worst of all possible worlds is to test the argument's run-time type
with a switch statement, if-else block, or the like (the "switch on
type" approach).

The next worst of all possible worlds is to code a different version of
the function for each derived type that may be an argument to it (the C
approach).

Hmm, this leads me to the next question:

given the case you have to call a function depending on two Instances of a
class, what shall we do then?
I append a small example to demonstrate you the Problem I have. Additionally
there is another constraint: no function overloading (a real-world reason
:-( ) for the final function "kicking" which is called out of main.

Please take a closer look at the function kicking. The if..else table is a
real horror. But how to solve it in an easier way?

Regards,
Patrick

// CODE STARTS

#include <iostream>

// small hierachy
// abstact base class, virtual for RTTI
class mama {
protected:
mama() {};
virtual ~mama() {};
};
class daughter : public mama {};
class son : public mama {};
class son2 : public mama {};

// small action functors
template <typename T1,typename T2> struct kick;
template <typename T> struct kick<T,T>
{
void operator()() { std::cout << "Look mum, I am kicking me." <<
std::endl; }
};
template <> struct kick<son,daughter>
{
void operator()() { std::cout << "He, do not kick girls !!!" <<
std::endl; }
};
template <> struct kick<son2,daughter>
{
void operator()() { std::cout << "He, do not kick girls !!!" <<
std::endl; }
};
template <> struct kick<daughter,son>
{
void operator()() { std::cout << "Aua, that hurts." << std::endl; }
};
template <> struct kick<daughter,son2>
{
void operator()() { std::cout << "Aua, that hurts." << std::endl; }
};
template <> struct kick<son,son2>
{
void operator()() { std::cout << "son2 is crying running to mama" <<
std::endl; }
};
template <> struct kick<son2,son>
{
void operator()() { std::cout << "son is kicking back" << std::endl; }
};

// function which shall be executed
// uuuuuhhhhh, this is really ugly,
// just for a call kick<instancetype(fam1_),instancetype(fam2_)>()();
// I really expect typos
void kicking(const mama & fam1_,const mama & fam2_)
{
std::cout << typeid(fam1_).name() << " tries to kick ";
std::cout << typeid(fam2_).name() << ": ";

if ( typeid(daughter) == typeid(fam1_) )
if ( typeid(daughter) == typeid(fam2_) )
kick<daughter,daughter>()();
else if ( typeid(son) == typeid(fam2_) )
kick<daughter,son>()();
else if ( typeid(son2) == typeid(fam2_) )
kick<daughter,son2>()();
else throw "Ups, what happend ?";
else if ( typeid(son) == typeid(fam1_) )
if ( typeid(daughter) == typeid(fam2_) )
kick<son,daughter>()();
else if ( typeid(son) == typeid(fam2_) )
kick<son,son>()();
else if ( typeid(son2) == typeid(fam2_) )
kick<son,son2>()();
else throw "Ups, what happend ?";
else if ( typeid(son2) == typeid(fam1_) )
if ( typeid(daughter) == typeid(fam2_) )
kick<son2,daughter>()();
else if ( typeid(son) == typeid(fam2_) )
kick<son2,son>()();
else if ( typeid(son2) == typeid(fam2_) )
kick<son2,son2>()();
else throw "Ups, what happend ?";
else throw "Ups, what happend ?";
}


// examples
int main()
{
kicking( son() , daughter() );
kicking( daughter() , son() );
kicking( son() , son() );
kicking( son() , son2() );
kicking( son2() , son() );
kicking( son2() , daughter() );

return 0;
}

// CODE ENDS
 
J

Jeff Schwab

stelios said:
OT:
And that's why typeID was maybe a not-so-good idea. It can be malused.


C approach would be that each object will have a member my_type,
which will be used in a switch/if. non-OOP. callbacks would be
the other solution, but that's why C++ is here for.

Yes, you're right. Having different C-style functions, as I suggested,
does not provide polymorphism. However, it is possible to get
compile-time polymorphism using overloaded functions:

struct A { void f( ) { /* do one thing */ } };
struct B { void f( ) { /* do another */ } };
struct C { void f( ) { /* do yet another */ } };

void f( A& a ) { a.f( ); }
void f( B& b ) { b.f( ); }
void f( C& c ) { c.f( ); }
You forgot inheritance?

struct Derrived1 : Base { ... };

Of course.

You're right again. Sorry for omission of vital detail.
Here on the other hand, we don't need inheritance indeed.
'Derrived1' is not really Derrived but just named so.

Just some more info for the OP:

If -the polymorphic- function "f" has more code:

void f (T &t)
{
...lots of code...
t.behave();
...lots of code...
}

The extra code will be duplicated with each specialization
and thus is not suitable for "embedded systems" where available
memory is 640kb.

Moreover, we are studying a simple case with just one virtual
function. If f() calls two virtuals, "behave1" and "behave2"
then OOP is definitelly superior.

These are all OOP approaches. ITYM "run-time indirection."

-Jeff
 
J

Jeremy Cowles

Jeff Schwab said:
IMNSHO:

The worst of all possible worlds is to test the argument's run-time type
with a switch statement, if-else block, or the like (the "switch on
type" approach).

The next worst of all possible worlds is to code a different version of
the function for each derived type that may be an argument to it (the C
approach).

The usual solution is to give the base class a virtual method, giving
each derived class the option of overriding the method:

struct Base { virtual void behave( ) { /* do one thing */ } };
struct Derived1 { void behave( ) { /* do another */ } };
struct Derived2 { void behave( ) { /* do yet another */ } };

void f( Base& b ) { b.behave( ); }


I am very new to C++, but I was wondering: what if the function f( ) does
not belong in the class? For example, say the class is logic for some
business process, which holds some displayable state, and function f( )
displays this state; wouldn't it be wrong to mix the GUI code in with the
logic?

Jeremy
 
N

Nils Petter Vaskinn

OT:
And that's why typeID was maybe a not-so-good idea. It can be malused.

So can sliced bread, but that doesn't mean it was a bad idea. Lots of good
thigs can be malused, the solution is not to do it.

Still I agree that most of the time an overloaded (I prefer virtual)
function will do better.
 
J

Jeff Schwab

Patrick said:
given the case you have to call a function depending on two Instances of a
class, what shall we do then?
I append a small example to demonstrate you the Problem I have. Additionally
there is another constraint: no function overloading (a real-world reason
:-( ) for the final function "kicking" which is called out of main.

Please take a closer look at the function kicking. The if..else table is a
real horror. But how to solve it in an easier way?

<snip> bunch of code with very amusing debug messages </>

Here's the approach I use. It relies on adding methods to the classes
involved. In fact, for this to work, the base class has to know which
sub-classes need to fit into the framework.

Btw, there's a good intro to this problem in _D&E_.

<code>

#include <iostream>

class daughter;
class son;
class son2;

class mama
{
public:
virtual void kick( mama& other ) =0;

virtual void get_kicked( daughter& kicker ) =0;
virtual void get_kicked( son& kicker ) =0;
virtual void get_kicked( son2& kicker ) =0;

protected:
mama( ) { };
virtual ~mama( ) { };
};

class daughter: public mama
{
public:
void kick( mama& victim );
void get_kicked( daughter& kicker );
void get_kicked( son& kicker );
void get_kicked( son2& kicker );
};

class son: public mama
{
public:
void kick( mama& victim );
void get_kicked( daughter& kicker );
void get_kicked( son& kicker );
void get_kicked( son2& kicker );
};

class son2: public mama
{
public:
void kick( mama& victim );
void get_kicked( daughter& kicker );
void get_kicked( son& kicker );
void get_kicked( son2& kicker );
};

void daughter::kick( mama& victim )
{
// Here, it is known that "this" points to a daughter.

victim.get_kicked( *this );
}

void daughter::get_kicked( daughter& kicker )
{
// I could kick myself!

std::cout << "daughter kicks daughter\n";
}

void daughter::get_kicked( son& kicker )
{
std::cout << "son kicks daughter\n";
}

void daughter::get_kicked( son2& kicker )
{
std::cout << "son2 kicks daughter\n";
}

void son::kick( mama& victim )
{
// Here, it is known that "this" points to a son.

victim.get_kicked( *this );
}

void son::get_kicked( daughter& kicker )
{
std::cout << "daughter kicks son\n";
}

void son::get_kicked( son& kicker )
{
std::cout << "son kicks son\n";
}

void son::get_kicked( son2& kicker )
{
// And this is my other brother Darryl...

std::cout << "son2 kicks son\n";
}

void son2::kick( mama& victim )
{
// Here, it is known that "this" points to a son2.

victim.get_kicked( *this );
}

void son2::get_kicked( daughter& kicker )
{
std::cout << "daughter kicks son2\n";
}

void son2::get_kicked( son& kicker )
{
std::cout << "son kicks son2\n";
}

void son2::get_kicked( son2& kicker )
{
std::cout << "son2 kicks son2\n";
}

void kicking( mama& fam1, mama& fam2 )
{
fam1.kick( fam2 );
}

int main( )
{
daughter d;
son s;
son2 s2;

kicking( d, d );
kicking( s, d );
kicking( s2, d );

kicking( d, s );
kicking( s, s );
kicking( s2, s );

kicking( d, s2 );
kicking( s, s2 );
kicking( s2, s2 );
}

</code>
 
J

Jeff Schwab

Sorry, I left an overloaded method (get_kicked). Please change

virtual void get_kicked( daughter& kicker ) =0;
virtual void get_kicked( son& kicker ) =0;
virtual void get_kicked( son2& kicker ) =0;

to

virtual void get_kicked_by_daughter( daughter& kicker ) =0;
virtual void get_kicked_by_son( son& kicker ) =0;
virtual void get_kicked_by_son2( son2& kicker ) =0;

and make similar adjusments to the derived classes.
 
P

Patrick Kowalzick

Hi Jeff,
<snip> bunch of code with very amusing debug messages </>

debug messages ?
Here's the approach I use. It relies on adding methods to the classes
involved. In fact, for this to work, the base class has to know which
sub-classes need to fit into the framework.

What I do not like at this solution is the fact, that each child MUST be
able to kick another child. But how can we implement a class "baby" which
neither kicks anybody nor could be kicked.

The abstract definition forces to include this, what is semantically wrong
(and political incorrect). Private or proteced declarations do not help
here, while the interface is derived and accesed via the base class (please
correct me if I am too wrong).
Btw, there's a good intro to this problem in _D&E_.

Ups, sorry. What is D&E ?
class daughter: public mama
{
public:
void kick( mama& victim );
void get_kicked( daughter& kicker );
void get_kicked( son& kicker );
void get_kicked( son2& kicker );
};

BTW, very exemplary variable naming :).
// I could kick myself!

OK, but I do not want to ;-).

Patrick
 
P

Patrick Kowalzick

Hi Jeff,
Sorry, I left an overloaded method (get_kicked). Please change.

No Problem :)

I just meant the overload of the "final" function "kicking". Hidden there
inside may reside as many overloads like you want :). So this is fine.

Patrick
 
V

Victor Bazarov

Patrick Kowalzick said:
[...]
Btw, there's a good intro to this problem in _D&E_.

Ups, sorry. What is D&E ?

"The {D}esign {and} {E}volution of C++" by Bjarne Stroustrup
(ISBN 0-201-54330-3). In case you haven't found it out yet...

V
 
J

Jeff Schwab

Patrick said:
debug messages ?

Your std::cout << "facetious quote" lines.
What I do not like at this solution is the fact, that each child MUST
be able to kick another child. But how can we implement a class "baby"
which neither kicks anybody nor could be kicked.

The abstract definition forces to include this, what is semantically
wrong (and political incorrect). Private or proteced declarations do
not help here, while the interface is derived and accesed via the base
class (please correct me if I am too wrong).

Multiple inheritance to the rescue!

(Forgive me, but I'm changing case to have the convention that
user-defined types begin with capital letters, so I can have a Kicker
class that doesn't interfere with variables called "kicker.")

Move the pure virtual kick/get_kicked declarations into a separate,
abstract class, called "Kicker." Derive Daughter, Son, and Son2 from
Mama *and* from Kicker. Then, Baby can be derived from Mama without
knowing anything about kicking.
 
J

Jeff Schwab

Jeremy said:
I am very new to C++, but I was wondering: what if the function f( ) does
not belong in the class? For example, say the class is logic for some
business process, which holds some displayable state, and function f( )
displays this state; wouldn't it be wrong to mix the GUI code in with the
logic?

namespace Logic
{
struct A { };
struct B { };
struct C { };
}

namespace Gui
{
void draw( A const& ) { }
void draw( B const& ) { }
void draw( C const& ) { }
}
 
J

Jeff Schwab

Jeff said:
namespace Logic
{
struct A { };
struct B { };
struct C { };
}

namespace Gui
{
void draw( A const& ) { }
void draw( B const& ) { }
void draw( C const& ) { }
}

D'oh!

Should be:

namespace Gui
{
void draw( Logic::A const& ) { }
void draw( Logic::B const& ) { }
void draw( Logic::C const& ) { }
}
 
P

Patrick Kowalzick

Hi Jeff,
Your std::cout << "facetious quote" lines.

You have brothers and sisters? I have.....a lot :).
Multiple inheritance to the rescue!

Ups, sorry, I left a hole open. I think I have to come out with some more
informations.

I have quite a lot of specialized template functors, depending on three
arguments - at all quite a lot functions. I use the templated functors to
get "compile-time" inheritance which works fine (really similar like my
first example).

[start OT]
[if reader = basic_hater goto end OT]

Now to the problem:
All the functions I have in C++ and they shall stay there. But sadly enough
I no have to go to Visual Basic. Inheritance and RTTI (not the ANSI one but
the managed C++ version :-( ) seems to me the only possibility to go there
without to much thinking.
One problem here is that my "compile-time" advantages are just dying, but I
do not mind. It should not be that overhead.

That all works fine on the VB-Side (I am so sorry):

Dim Son = New familyfromcpp.son
Dim Daughter = New familyfromcpp.daughter

Dim action As Object
action = New familyfromcpp.kickingclass
action.kicking(Son,Daughter)
action.kicking(Daughter,Son)

Quite an easy syntax calling the powerful C++ library *gg*. In fact daughter
and son must mot be classes, they could be constants or something else as
well. But I like the version with classes for some reasons.

[end OT - Please forgive me. Do not ban me. Please. ]

[copy paste]
Multiple inheritance to the rescue!

I am sorry (for me). No multiple inheritance. No Policies. Back to the
roots.

The keyword I found yet (Thx Chris) is "double dispatching" (in fact
multiple dispatching) and the belonging chapters in the books "Modern C++
Design" and "Effective C++". But I find both rather complicated approaches
and I am searching an easier one. Anyway I like most the Brute-force
approach from Andrei.

So coming back to:
The worst of all possible worlds is to test the argument's run-time type
with a switch statement, if-else block, or the like (the "switch on
type" approach).

Exactly this could be very useful here. But I want to avoid the huge if else
blocks. And I wanted to avoid macro-programming but it could be more
understandable:

#define call_kick(type1,type2) \
if ( typeid(type1) == typeid(fam1_) && typeid(type2) == typeid(fam2_)) \
kick<type1,type2>()(); else

// function which shall be executed
void kicking(const mama & fam1_,const mama & fam2_)
{
std::cout << typeid(fam1_).name() << " tries to kick ";
std::cout << typeid(fam2_).name() << ": ";

call_kick(daughter,daughter)
call_kick(daughter,son)
call_kick(daughter,son2)
call_kick(son,daughter)
call_kick(son,son)
call_kick(son,son2)
call_kick(son2,daughter)
call_kick(son2,son)
call_kick(son2,son2)
throw "Ups, what happend ?";
}

Macros for the rescue !!!

Regards,
Patrick
 
S

stelios xanthakis

Nils Petter Vaskinn said:
So can sliced bread, but that doesn't mean it was a bad idea. Lots of good
thigs can be malused, the solution is not to do it.

Still I agree that most of the time an overloaded (I prefer virtual)
function will do better.

Thing is, typeID *can* be achieved with virtuals+static local variables.

So either give us "virtual variables" (variables that are in the virtual
table: meta programming), or don't. typeID is in the middle.

the truth is that typeID was required because exceptions have to throw
objects (for some reason). So typeID would definitelly be part of the
language. Book writers should warn about this situation: typeID it's not
a serious feature; it was there and they decided to give it to the programmers
for debugging purposes. Apart from that, typeID should be avoided like goto.

stelios
 

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

Forum statistics

Threads
474,156
Messages
2,570,878
Members
47,405
Latest member
DavidCex

Latest Threads

Top