5

I have a class hierarchy where I want to introduce a method template that would behave like if it was virtual. For example a simple hierarchy:

class A {
  virtual ~A() {}

  template<typename T>
  void method(T &t) {}
};

class B : public A {
  template<typename T>
  void method(T &t) {}
};

Then I create object B:

A *a = new B();

I know I can get the type stored in a by typeid(a). How can I call the correct B::method dynamically when I know the type? I could probably have a condition like:

if(typeid(*a)==typeid(B))
    static_cast<B*>(a)->method(params);

But I would like to avoid having conditions like that. I was thinking about creating a std::map with typeid as a key, but what would I put as a value?

Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96

5 Answers5

6

You can use the "Curiously Recurring Template Pattern" http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Using this pattern, the base class takes the derived class type as a template parameter, meaning that the base class can cast itself to the derived type in order to call functions in the derived class. It's a sort of compile time implementation of virtual functions, with the added benefit of not having to do a virtual function call.

template<typename DERIVED_TYPE>
class A {
public:
    virtual ~A() {}

    template<typename T>
    void method(T &t) { static_cast<DERIVED_TYPE &>(*this).methodImpl<T>(t); }
};

class B : public A<B>
{
friend class A<B>;

public:
    virtual ~B() {}

private:
    template<typename T>
    void methodImpl(T &t) {}
};

It can then be used like this...

int one = 1;
A<B> *a = new B();
a->method(one);
Alan
  • 1,895
  • 1
  • 10
  • 9
  • 1
    there will be no base class in this case, so you can't store these classes ex. in a vector because class A is a template. But You can inherit from a common ABase but at that case you can't access the method from the Base. – Industrial-antidepressant Sep 30 '11 at 17:11
  • @Industrial-antidepressant yes there are obviously downsides to this method, such as not being able to store different derived types in a container, but this is still a usefule design pattern to know about. – Alan Oct 01 '11 at 16:35
2

Is there any common code you could extract and make virtual?

class A {
  virtual ~A() {}

  template<typename T>
  void method(T &t) 
  {
      ...
      DoSomeWork();
      ...
  }

  virtual void DoSomeWork() {}
};

class B : public A {
  virtual void DoSomeWork() {}
};
DanDan
  • 10,462
  • 8
  • 53
  • 69
1

As you may know, you cannot have templates for virtual functions, since the entirety of the virtual functions is part of the class type and must be known in advance. That rules out any simple "arbitrary overriding".

If it's an option, you could make the template parameter part of the class:

template <typename T> class A
{
protected:
  virtual void method(T &);
};

template <typename T> class B : public A<T>
{
  virtual void method(T &); // overrides
};

A more involved approach might use some dispatcher object:

struct BaseDispatcher
{
  virtual ~BaseDispatcher() { }
  template <typename T> void call(T & t) { dynamic_cast<void*>(this)->method(t); }
};
struct ConcreteDispatcher : BaseDispatcher
{
  template <typename T> void method(T &);
};

class A
{
public:
  explicit A(BaseDispatcher * p = 0) : p_disp(p == 0 ? new BaseDispatcher : p) { }
  virtual ~A() { delete p_disp; };
private:
  BaseDispatcher * p_disp;
  template <typename T> void method(T & t) { p_disp->call(t); }
};

class B : public A
{
public:
  B() : A(new ConcreteDispatcher) { }
  // ...
};
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I cannot do that. I really need a method template. – Juraj Blaho Sep 30 '11 at 12:37
  • @JurajBlaho: I added another idea. – Kerrek SB Sep 30 '11 at 12:40
  • @JurajBlaho: Well, [making member templates virtual just doesn't work in C++](http://stackoverflow.com/questions/2354210/can-a-member-function-template-be-virtual/2354671#2354671), so you will have to rethink your approach and make some concessions to the language's limitation. – sbi Sep 30 '11 at 12:45
  • Actually, never mind, the second idea doesn't work. You still need some sort of polymorphism somewhere, which cannot be templated. – Kerrek SB Sep 30 '11 at 12:53
1

Oops. Initially answered at the wrong question - ah well, at another question

After some thinking I recognized this as the classic multi-method requirement, i.e. a method that dispatches based on the runtime type of more than one parameter. Usual virtual functions are single dispatch in comparison (and they dispatch on the type of this only).

Refer to the following:

  • Andrei Alexandrescu has written (the seminal bits for C++?) on implementing multi-methods using generics in 'Modern C++ design'
    • Chapter 11: "Multimethods" - it implements basic multi-methods, making them logarithmic (using ordered typelists) and then going all the way to constant-time multi-methods. Quite powerful stuff !
  • A codeproject article that seems to have just such an implementation:
    • no use of type casts of any kind (dynamic, static, reinterpret, const or C-style)
    • no use of RTTI;
    • no use of preprocessor;
    • strong type safety;
    • separate compilation;
    • constant time of multimethod execution;
    • no dynamic memory allocation (via new or malloc) during multimethod call;
    • no use of nonstandard libraries;
    • only standard C++ features is used.
  • C++ Open Method Compiler, Peter Pirkelbauer, Yuriy Solodkyy, and Bjarne Stroustrup
  • The Loki Library has A MultipleDispatcher
  • Wikipedia has quite a nice simple write-up with examples on Multiple Dispatch in C++.

Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):

// Example using run time type comparison via dynamic_cast

struct Thing {
    virtual void collideWith(Thing& other) = 0;
}

struct Asteroid : Thing {
    void collideWith(Thing& other) {
        // dynamic_cast to a pointer type returns NULL if the cast fails
        // (dynamic_cast to a reference type would throw an exception on failure)
        if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
            // handle Asteroid-Asteroid collision
        } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
            // handle Asteroid-Spaceship collision
        } else {
            // default collision handling here
        }
    }
}

struct Spaceship : Thing {
    void collideWith(Thing& other) {
        if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) {
            // handle Spaceship-Asteroid collision
        } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) {
            // handle Spaceship-Spaceship collision
        } else {
            // default collision handling here
        }
    }
}

Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
0

I think the only solution is the http://en.wikipedia.org/wiki/Visitor_pattern

See this topic: How to achieve "virtual template function" in C++

Community
  • 1
  • 1