upcasting of baseclasspointer

  • Thread starter Patrick Kowalzick
  • Start date
P

Patrick Kowalzick

Dear all,

I think I have a very common problem:

How shall I upcast an object out of a heterogeneous set. Even worse, I want
to avoid the built in C++ RTTI for some reasons. So I thought every object
which could be upcastet shall get an ID. If an upcast is needed I use a
switch table or a if_else_if_else construct. The switch table is quite
nicer, but while I want to upcast more than one object at a time it needs
more accurate work for the IDs to get unique cases.

Until now, I use an extra namespace where my IDs are stored. The only reason
you can find inside the macro ADD_CASES I wrote: I use the same name for the
class and for the ID. This macro is written to avoid the repepetition of the
possible cases (which must not match all permutations, see not allowed
case).

I am sorry to post so much code. The foo-function is not allowed to be
virtual. Typelist may not be a good idea (not sure), because my real types
are templated.

Coming to my questions:

How do you solve these kinds of problems ?
Any suggestions for my code ? Did I overlook some ugly sideeffects ?

Regards,
Patrick

#include <iostream>
#include <vector>
#include <utility>



// Polymorphic class hierachy
namespace ID {
const int A = 0;
const int B = 1;
}

class AB {
public:
virtual ~AB() {}
virtual int ID() = 0;
};

class A : public AB {
public:
A() {
std::cout << "Constructor A called" << std::endl;
}
virtual ~A() {
std::cout << "Destructor A called" << std::endl;
}
virtual int ID() { return ID::A; }
void foo() {
std::cout << "Foo A called" << std::endl;
}
};

class B : public AB {
public:
B() {
std::cout << "Constructor B called" << std::endl;
}
virtual ~B() {
std::cout << "Destructor B called" << std::endl;
}
virtual int ID() { return ID::B; }
void foo() {
std::cout << "Foo B called" << std::endl;
}
};

// 2.Polymorphic class hierachy
namespace ID {
const int C = 0;
const int D = 2;
}

class CD {
public:
virtual ~CD() {}
virtual int ID() = 0;
};

class C : public CD {
public:
C() {
std::cout << "Constructor C called" << std::endl;
}
virtual ~C() {
std::cout << "Destructor C called" << std::endl;
}
virtual int ID() { return ID::C; }
void foo() {
std::cout << "Foo C called" << std::endl;
}
};

class D : public CD {
public:
D() {
std::cout << "Constructor D called" << std::endl;
}
virtual ~D() {
std::cout << "Destructor D called" << std::endl;
}
virtual int ID() { return ID::D; }
void foo() {
std::cout << "Foo D called" << std::endl;
}
};


// END OF CLASS DEFINITIONS


#define SWITCH_FOO_CASE(CLASS1,CLASS2,BASE1,BASE2) \
case(ID::CLASS1 + ID::CLASS2) : \
static_cast<CLASS1 *>(BASE1)->foo(); \
static_cast<CLASS2 *>(BASE2)->foo(); \
break;

// only here the order for the table is from importance
#define ADD_CASES(CASE,BASE1,BASE2) \
CASE(A,C,BASE1,BASE2) \
CASE(B,C,BASE1,BASE2) \
CASE(B,D,BASE1,BASE2)


int main() {

// CASE 1
// ONLY ONE CLASS
std::vector<AB *> vAB;

// storage
vAB.push_back(new A);
vAB.push_back(new B);

// easy example for switch
for (size_t i=0;i<vAB.size();++i)
switch (vAB->ID()) {
case(0) : static_cast<A *>(vAB)->foo();
break;
case(1) : static_cast<B *>(vAB)->foo();
break;
default : std::cout << "Ups....something is wrong" << std::endl;
}

for (size_t i=0;i<vAB.size();++i) if (vAB) delete vAB;


// CASE 2
// TWO DIFFERENT CLASSES
std::vector< std::pair<AB *,CD *> > vP;

// storage
vP.push_back( std::pair<AB *,CD *>(new A,new C) );
vP.push_back( std::pair<AB *,CD *>(new A,new D) );
vP.push_back( std::pair<AB *,CD *>(new B,new C) );
vP.push_back( std::pair<AB *,CD *>(new B,new D) );

// more complicated switch constructed by macro
for (size_t i=0;i<vP.size();++i)
switch (vP.first->ID() + vP.second->ID()) {
ADD_CASES(SWITCH_FOO_CASE,vP.first,vP.second)
default : std::cout << "This case is not allowed" << std::endl;
}

for (size_t i=0;i<vP.size();++i) {
if (vP.first) delete vP.first;
if (vP.second) delete vP.second;
}

return 0;
}
 
I

Isaac

Something like

class Base{
public:
virtual void *CastFn() const = 0;
};


#define MYUPCAST(a) \
public: static a *Upcast(Base *pbase) \
{
\
if( pbase->CastFn() == Upcast ) \
return static_cast<a *>(pbase); \
return 0;
\
}
\
virtual void *CastFn(){ return Upcast; } \


class A: public Base
{
MYUPCAST(A)
...
}

class B: public Base
{
MYUPCAST(B)
...
}

Base *p;

A *pa = A::Upcast(p);
if( pa )
{
// It's an A
}

B *pb = B::Upcast(p)
if(pb)
{
// It's a B
}

Pro: The compiler gives each class it's own, unique ID, by providing a
pointer to a static function.
Contra: The classes all have to be derived from base

Isaac
 
P

Patrick Kowalzick

Hello Isaac,

I like the solution with the pointer to a static function.

The problem with this solution is, that I do not find a way to integrate
this in a "switch" table. The case with the ifs works fine but in my program
I have 54 different cases....and it is getting more. You could assume poor
design :) ... but I am trying.
#define MYUPCAST(a) \
public: static a *Upcast(Base *pbase) \
{
\
if( pbase->CastFn() == Upcast ) \
return static_cast<a *>(pbase); \
return 0;
\
}
\
virtual void *CastFn(){ return Upcast; } \

I think the last line should be:
virtual void *CastFn() const { return Upcast; }

And in my OP I forgotten quite more const:should be
virtual int ID() const = 0;
and as well in all inherited classes as well.

Thanks a lot,
Patrick
 

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,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top