5

I'm having a problem in C++ with calling a function of a derived class while having a pointer to the base class.

EDIT: Some answers referred me to CRTP

but my point is that I need to have a pointer to the "Base*" class not "Base*" because I'm unaware of the type currently being handled (The current instance is created from some sort of a factory).

Classes:

class Base 
{
..
template<typename T>
func (T arg) { ... };
};

class Derived1 : public Base
{
...
template<typename T>
func (T arg) { ... };
};


class Derived1 : public Base
{
...
template<typename T>
func (T arg) { ... };
};

Usage:

int main()
{
   Base* BasePtr = new Derived1();

   // The expected function to be called is Derived1::func<int>()
   BasePtr->func<int>();

   return 0; // :)
}

I can't make func virtual because the language does not support virtual template function.

It is only allowed if only the class is have template arguments but not if the function within it has template arguments.

I have seen a similar problem resolved within Boost.Serialization but couldn't understand the solution.

Thanks,

Koby Meir

Alderath
  • 3,761
  • 1
  • 25
  • 43
koby meir
  • 437
  • 7
  • 16
  • 1
    The possible solution may be to add one virtual function to your ierarchy (for example virtual destructor) and then use `dynamic_cast` to determine actual type of you `BasePtr` object. `dynamic_cast(BasePtr)` returns `NULL` if BasePtr isn't pointed to `Derived1` class object and `Derived1*` otherwise. After getting pointer to `Derived1` you can call it's member functions. – beduin May 17 '11 at 05:55

3 Answers3

8

Implement Curiously recurring template pattern(CTRP).

Illustration:

template<typename D>
class Base 
{
public:
    template<typename T>
    void func (T arg) 
    {
         static_cast<D*>(this)->func(arg);
    }
};

class Derived1 : public Base<Derived1>
{
public:
    template<typename T>
    void func (T arg) { /*...*/ }
};


Base<Derived1> *basePtr = new Base<Derived1>();
basePtr->func(100);
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Hi, Thanks for the quick answer. but my point is that i need to have a pointer to the Base class not Base because i'm unaware of the type currently being handled. – koby meir May 17 '11 at 07:33
  • @koby meir: Alright. I understand that. By the way, why do you want `func` to be function template? If you make it virtual function, then the solution is simple. But then you also need *template*. So it all depends on the question : why do you need template? – Nawaz May 17 '11 at 08:00
  • @koby: BTW, the solution provided by @David seems plausible. Did you consider that? – Nawaz May 17 '11 at 08:01
  • @Nawaz: The solution by @David seems the closest to what i need , but as i wrote to David. It required the "Visitor" to know all the Derived types from the Base – koby meir May 17 '11 at 08:18
  • 1
    @koby: You can use *typelist* and recursive base for visitor to solve this problem. I would be fun to do so. – Nawaz May 17 '11 at 08:20
  • @Nawaz: I would try @David solution with the typelist and see how it goes, can you refer me to a good typelist solution/article? – koby meir May 17 '11 at 08:27
6

The two existing solutions trade dynamic polymorphism for static polymorphism. Without more details on the problem at hand, it is not possible to know whether that is a valid approach or not, as it basically breaks the polymorphic hierarchy: with CRTP there is no single base class, but rather a family of them. You cannot hold objects of Derived1 and Derived2 in the same container as they are unrelated... It is a fine solution if all you need is to share the code, but not if you need dynamic polymorphism. Take a look at the Visitor pattern and at double-dispatch for similar problems.

If you need dynamic polymorphism, you could try to implement double dispatch (it is a pain, but feasible if the hierarchy is small enough. Basically create two different hierarchies, one rooted at Base and another that serves as some short of manual dispatcher. The hierarchy rooted at Base will have a virtual method apply, and the second hierarchy will have virtual functions for each one of the types in the first hierarchy:

class Base;
class Derived1;  // inherits from Base, implements Visitor
class Derived2;  // inherits from either Base or Derived2
struct Visitor {
   virtual void visit( Base& ) = 0;     // manually unrolled for all types
   virtual void visit( Derived1& ) = 0;
   virtual void visit( Derived2& ) = 0;
};
struct Base {
   virtual void apply( Visitor& v ) {   // manually replicate this in Derived1, 2
      v.visit( *this );
   }
   template <typename T> void foo(T);   // implement 
};

template <typename T>
struct FooCaller : Visitor {
    T& ref_value;
    FooCaller( T& v ) : ref_value(v) {}
    template <typename U> void call_foo( U& o ) {
       o.foo(ref_value);
    }
    virtual void visit( Base & b )      { call_foo(b); }
    virtual void visit( Derived1 & d1 ) { call_foo(d1); }
    virtual void visit( Derived2 & d2 ) { call_foo(d2); } 
};

The names I have used are common in the Visitor pattern, and this approach is quite similar to that pattern (I don't dare call it the Visitor pattern, but the approach is similar, so I just borrowed the naming convention).

User code would be similar to:

int main()                     // main returns int, not void!!!
{
   Base* BasePtr = new Derived1();
   int i = 5;
   FooCaller<int> c(i)
   BasePtr->apply(c);          // [1] magic happens here
}

The requirement of declaring i and c before hand can be released by changing (if possible) the arguments to the functions from references to const-references. The actual magic is that in [1] the C++ single dispatch mechanism sill dispatch the call to Derived1::apply, since that is the dynamic type of the object pointed by BasePtr. At that point it will call Visitor::visit( Derived1& ) with itself as the argument. That will again be dispatched through the single dispatch mechanism to FooCaller<int>::visit( Derived1& ), and at that point both objects have been resolved to their static types. When FooCaller<int>::visit calls call_foo the argument U is deduced to be Derived1, when it calls Derived1::foo the argument is deduced to be int and it ends up calling Derived1::foo<int>... though a couple of loops and indirections...

Depending on your particular use case this might be too complex (if static polymorphism like CRTP would work) or too hard to maintain (if the hierarchy is big: for each new element in the Base hierarchy you will have to update all types in the hierarchy of the Visitor), so if you can avoid this complexity, perfect. In some cases, though, you need this.

Also note that this is the most complex fully dynamic solution, there are other options in between, depending on what is it that you need to be runtime polymorphism... It might be the case that your hierarchy models a visitor of shorts, and that you only need to manually unroll the different virtual functions that will dispatch to the template internally, in which case half of the above complexity will be gone.

Also note that this is quite unusual in C++, and that if you explain the actual problem at hand, there might be better simpler solutions, what you have stated are the requirements of your solution to the original problem: dynamically dispatch to a template.

Nawaz
  • 353,942
  • 115
  • 666
  • 851
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Thanks, But this solution requires me to let the Visitor to know all the Derived types. Is there a way to bypass that? – koby meir May 17 '11 at 07:51
  • @David: You should use the phrase *dynamic polymorphism* instead of *real polymorphism* or *actual polymorphism*. – Nawaz May 17 '11 at 08:04
  • @koby Robert Martin explains how the acyclic visitor pattern works: http://www.objectmentor.com/resources/articles/acv.pdf – Jan May 17 '11 at 08:23
  • 3
    Also, the implementation through visitors could be hidden inside template foo: `template void Base::foo(T t) { FooCaller c(t); apply(c); }` – Alsk May 17 '11 at 08:52
  • @Nawaz: thanks for the editing. @Alsk, right that makes the usage much simpler. – David Rodríguez - dribeas May 17 '11 at 10:31
  • @koby: you should try to explain the use case, there might be simplifications that can be made based on the restrictions in your use case, that will help in providing a simpler interface/implementation. – David Rodríguez - dribeas May 17 '11 at 10:33
3

Check this out, it will help you implement the CRTP.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533