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[i]->ID()) {
case(0) : static_cast<A *>(vAB[i])->foo();
break;
case(1) : static_cast<B *>(vAB[i])->foo();
break;
default : std::cout << "Ups....something is wrong" << std::endl;
}
for (size_t i=0;i<vAB.size();++i) if (vAB[i]) delete vAB[i];
// 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[i].first->ID() + vP[i].second->ID()) {
ADD_CASES(SWITCH_FOO_CASE,vP[i].first,vP[i].second)
default : std::cout << "This case is not allowed" << std::endl;
}
for (size_t i=0;i<vP.size();++i) {
if (vP[i].first) delete vP[i].first;
if (vP[i].second) delete vP[i].second;
}
return 0;
}