views:

364

answers:

2

Have a base class A, and a derived class B which overrides function template Func:

class A
{
          A() {...};
          ~A() {};

          template <class T>
          void Func(const String &sInput, T &tResult)
          {...}
};

class B : public A
{
          B() {...}
          ~B() {};

          template <class T>
          void Func(const String &sInput, T &tResult)
          {...}
};

(Note that Func is non-virtual, given the lack of support in C++ for templated virtual functions.)

Now have a mainprog API, class M:

class M
{
  M(boost::shared_ptr<A> &pInterfaceInput): pInterface(pInterfaceInput)
  {}  

  template <class T>
  Evaluate(const String &sInput, T &tResult) 
  {
    pInterface->Func<T>(sInput, tResult);
  }

  private:
  const boost::shared_ptr<A> pInterface;
};

I want the function Evaluate here to support calls to functions on base class A or any of its derived classes (such as B). This class was written with polymorphism in mind before I re-designed class A and B to have templated functions.

Now the problem here is that if I pass a shared pointer of the base type to the derived type then Func of the base class will be called, not the derived class being pointed to.

How do I get around the lack of dynamic polymorphism here? I've considered making class M a class template on the shared pointer type and having a static_cast in the constructor to ensure this type is of the base class type (A) or of a derived class.

What's the nicest way to do this? I'd prefer not to modify classes A and B to get around this problem but all suggestions are welcome.

Thanks.

+3  A: 

Sounds like a double dispatch problem. Perhaps this would be a good place to implement the visitor pattern?

For example, create a class Evaluator, and for each T a subclass ConcreteEvaluator<T>. Give A and B methods that visit the Evaluator. Something like:

class Evaluator 
{
    virtual void visit_A(A* object);
    virtual void visit_B(B* object);
};

template <typename T>
class ConcreteEvaluator : public Evaluator
{
    public:
    String* input_reference;
    T& result_reference;

    ConcreteEvaluator(String& input_reference_,T& result_reference_) :
        input_reference(input_reference_), 
        result_reference(result_reference_) {}

    virtual void visit_A(A* object) {
        object->Func(input_reference,result_reference);
    }
    virtual void visit_B(B* object) {
        object->Func(input_reference,result_reference);
    }
 }

class A
{
...
virtual void apply_evaluator(Evaluator *eval) {eval->visit_A(this);}
...
}

class B
{
...
virtual void apply_evaluator(Evaluator *eval) {eval->visit_B(this);}
...
}

For each subclass of A, a new method must be added to ConcreteEvaluator, so that this technique works best if A's class hierarchy is stable. And for each subclass of A, it must have an apply_evaluator function defined properly.

On the other hand, this may be total overkill. For about the same amount of work, you could always just pay the price to update M::Evaluate:

class M
{
    ...
    void Evaluate(const String& sInput, T& tResult)
    {
        // try to downcast to each subclass of A.  Be sure to check
        // sub-subclasses first
        try
        {
            dynamic_cast<B*>(pInterface.get())->Func(sInput, tResult);
            return;
        }
        catch (std::bad_cast& ) { }
        ...
        // nothing worked.  It must really be an A
        pInterface->Func(sInput,tResult);
    }
    ...
};
Managu
A: 

I've show in the question Templatized Virtual function how to use type erasure to get some of the effects of virtual member function. Depending on what you want to do in Func(), you can use the same technique here.

AProgrammer