Re: polymorphism problem: common signatures with no common parent
From: Neil Zanella (nzanella_at_cs.mun.ca)
Date: 03/28/04
- Next message: Neil Zanella: "implicit typename"
- Previous message: Siemel Naran: "Re: Public data members = bad programming etc."
- In reply to: Leor Zolman: "Re: polymorphism problem: common signatures with no common parent"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: 27 Mar 2004 16:57:55 -0800
Leor Zolman <leor@bdsoft.com> wrote in message
> This is pushing the envelope of my experience, so perhaps there's a design
> pattern or trick I'm not seeing...but given that the base class does not
> contain those functions you're trying to call, the only way I can think of
> to do this is to employ RTTI and do a brute-force query of the type. Here's
> a main function that works (just don't forget to include <typeinfo> at the
> top, and make sure RTTI is enabled. For MSVC, I needed the /GR option):
>
> int main() {
>
> std::vector<AB *> stuff = getStuff();
>
> // now execute foo and bar on all members of collection stuff
>
> for (std::vector<AB *>::iterator it = stuff.begin();
> it != stuff.end(); ++it)
> {
>
> if (typeid(**it) == typeid(A))
> {
> A *p = static_cast<A *>(*it);
> p->hello();
> p->foo();
> p->bar();
> }
> else if (typeid(**it) == typeid(B))
> {
> B *p = static_cast<B *>(*it);
> p->hello();
> p->foo();
> p->bar();
> }
> }
> }
>
>
> It's ugly, I know. I can't even think of a way to represent the types in a
> data structure and then just look them up, because using a base class
> pointer in this case to hold the result of the lookup wouldn't work (for
> the same reason we couldn't just call the functions directly via the base
> class pointer). I'd probably like to see a better way even more than you
> would (this at least gives you a way to get the task done...)
> -leor
Hmmm... there must be something that can be done about it. I am not
particularly fond of the use of switch statements and conditional
statements of the kind given above in object-oriented programming.
On the other hand, I don't know whether the solution I describe
below just complicates matters further. I don't claim it to be
neater, but I am sure there is a better pattern for this kind
of thing, perhaps some generic classes with more template
arguments or something like that. After all, the problem
is very general. You have a class AB with no foo methods,
and a bunch of subclasses, each having the foo methods,
and you cannot touch neither the classes nor the collection.
You want to process the subclasses in the given collection
polymorphically. The solution I give below, sort of applies
to this particular case, but cannot be used as machinery
against other problems of similar kind without modifying
the classes. I would need some further templatization to
do so I suppose...
Basically, I should be able to specify the stuff class,
and template arguments AB, A, B, and whatever other
subclasses of AB, and whatever methods are shared
among A, B, etc... (methods foo() and bar() etc...)
and get back my commonInstance collection ready to
be processed polymorphically. That is the generic
case which I would like to solve... for the time
being here is an imperfect solution...
Further contributions welcome...
Best Regards,
Neil
#include <iostream>
#include <typeinfo>
#include <vector>
/// BEGIN "UNTOUCHABLE" CLASSES AND DATA INSTANTIATORS ///////////////
/* untouchable base class */
class AB {
public:
virtual void hello() = 0;
virtual ~AB() { }
};
/* one of many untouchable derived classes with commonly named
and signed methods not found in base class */
class A: public AB {
public:
A(int x): AB(), x(x) { }
virtual void hello() { std::cout << "A: hello" << std::endl; }
void foo() { std::cout << "A: foo: " << x + 1 << std::endl; }
void bar() { std::cout << "A: bar: " << 2 * x << std::endl; }
private:
int x;
};
/* one of many untouchable derived classes with commonly named
and signed methods not found in base class */
class B: public AB {
public:
B(double x): AB(), x(x) { }
virtual void hello() { std::cout << "B: hello" << std::endl; }
void foo() { std::cout << "B: foo: " << x + 2 << std::endl; }
void bar() { std::cout << "B: bar: " << 4 * x << std::endl; }
private:
double x;
};
/* some untouchable method returning an arbitrary vector
of base class pointers to derived class objects */
std::vector<AB *> &getStuff() {
static std::vector<AB *> stuff;
stuff.push_back(new A(10));
stuff.push_back(new B(20));
return stuff;
}
/// END "UNTOUCHABLE" CLASSES AND DATA INSTANTIATORS /////////////////
// Class containing common method names.
class Common {
public:
virtual ~Common() { }
virtual void foo() = 0;
virtual void bar() = 0;
};
// Class containing instances of classes having common methods.
template<class T>
class CommonInstance: public Common {
public:
CommonInstance(T &instance): instance(instance) { }
virtual void foo() { instance.foo(); }
virtual void bar() { instance.bar(); }
private:
T &instance;
};
// Generic class used for conversion.
class Converter {
public:
template<class T>
void addClass() {
conditionalConverters.push_back(new
ConditionalConverterInstance<T>);
}
Common *convert(AB *parent) {
Common *common = 0;
for (ConditionalConverters::iterator it =
conditionalConverters.begin();
it != conditionalConverters.end(); ++it)
if (common = (*it)->convert(parent))
break;
return common;
}
private:
class ConditionalConverter {
public:
virtual Common *convert(AB *parent) = 0;
};
template<class T>
class ConditionalConverterInstance: public ConditionalConverter {
public:
virtual Common *convert(AB *parent) {
return typeid(*parent) == typeid(T) ?
new CommonInstance<T>(*static_cast<T *>(parent)) : 0;
}
};
typedef std::vector<ConditionalConverter *> ConditionalConverters;
ConditionalConverters conditionalConverters;
};
// Custom method used to convert so it is polymorphically processable.
std::vector<Common *> &getCommonStuff(const std::vector<AB *> &stuff)
{
static std::vector<Common *> commonStuff;
// Construct a custom converter which can then be used to convert
each
// untouchable instance to something more polymorphically
manageable.
Converter converter;
converter.addClass<A>();
converter.addClass<B>();
// Construct the custom collection.
for (std::vector<AB *>::const_iterator cit = stuff.begin();
cit != stuff.end(); ++cit)
commonStuff.push_back(converter.convert(*cit));
// Return the custom collection.
return commonStuff;
}
// Use polymorphic processing.
int main() {
// Retrieve the collection of untouchables.
std::vector<AB *> stuff = getStuff();
// Construct a collection suitable for polymorphic processing.
std::vector<Common *> commonStuff = getCommonStuff(stuff);
// Process the collection polymorphically.
for (std::vector<Common *>::iterator it = commonStuff.begin();
it != commonStuff.end(); ++it) {
(*it)->foo();
(*it)->bar();
}
}
/* RESULT OF RUNNING PROGRAM */
//$ gcc -o example example.cpp
//$ ./example
//A: foo: 11
//A: bar: 20
//B: foo: 22
//B: bar: 80
- Next message: Neil Zanella: "implicit typename"
- Previous message: Siemel Naran: "Re: Public data members = bad programming etc."
- In reply to: Leor Zolman: "Re: polymorphism problem: common signatures with no common parent"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|