Re: polymorphism problem: common signatures with no common parent

From: Neil Zanella (nzanella_at_cs.mun.ca)
Date: 03/28/04


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



Relevant Pages

  • Re: OO refactoring trial ??
    ... As long as the concrete Python classes follow the form ... other common-to-all-taunts behavior, perhaps a common constructor, or ... in both Python and Java. ... return values derive from a common base class, ...
    (comp.lang.python)
  • Re: Inheritance and object factories...HELP!!
    ... In the pimpl idiom you would have a Concrete class which contains a member ... class Concrete: public Foo ... deriv2, etc) and only cares about the public interface of the base class, ... > The member function foo creates a new object on the stack and returns it ...
    (comp.lang.cpp)
  • Re: Downcasting - whats the problem?
    ... Root of the problem is while trying to model a problem domain we do abstractions, ... In the process we do refinement to identified classes and ... into a separate class (base class). ... When we want to invoke certain behavior on all objects that have common behavior. ...
    (comp.object)
  • Re: Multiple ineritance question
    ... another - through base class subobject 'B'. ... function 'foo' from the former has 'A_Impl::foo' as final overrider, ... as final overrider, which is _pure_. ...
    (comp.lang.cpp)
  • Re: Virtual base class constructor
    ... overrides a virtual function that it inherits from a virtual base class, ... virtual void foo() ...
    (microsoft.public.vc.language)