function dispatch based on template arguments at run-time

Hi,

I have a non-generic base class with a generic derived class and a generic function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Base {
    virtual void foo() {} // just to make it polymorphic
};

template <class T>
class Derived : public Base {
public:
    T t;
};

template <class K>
void function(K k) {
    ...
}

Let's suppose that I have a Base* variable, pointing to a Derived object. Is it possible to pass it to the function? One solution is to use dynamic_cast, like below:

1
2
3
4
5
6
7
8
Base* b = new Derived<int>();
...

if (Derived<int>* d = dynamic_cast<Derived<int>*>(b))
    function(d->t);
else if (Derived<long>* d = dynamic_cast<Derived<long>*>(b))
    function(d->t);
...

This works, but it is not very nice, all the "if-else-if" branches look the same, and the list in my case would be possibly infinite. (The template argument is another generic type.)

Is there a more general solution?
Thanks!
What is that function supposed to do? Can you make it a virtual member function?
The function has to display an image. The argument is a "smart pointer" to an ITK image. itk::Image<PixelType, Dimension>::Pointer.
The Derived class contains the smart pointer to the image.

I wanted to separate the GUI from the model.
Otherwise, making it a virtual member function does not solve the problem, but you are right that it is simpler this way:

class Base {
virtual void f() {}
};

template <class T>
class Derived : public Base {
public:
T t;
void function() { ... }
};

int main() {
Base* b = new Derived<int>;
...
if (Derived<int>* d = dynamic_cast<Derived<int>*>(b)) {
d->function();
}
else if (Derived<long>* d = dynamic_cast<Derived<long>*>(b)) {
d->function();
}
...
delete b;
return 0;
}

So, how to invoke the member function of a generic class through a pointer, if the pointer was declared with the non-generic base class?
Is it possible anyway, even if it is not compile-time safe?
First, are you sure that all these templates are not causing code bloat. Anyway. You say that you can not use virtual function as m4ster r0shi suggests. Why? How about this code as a reference:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <typeinfo>
#include <memory>

struct Base {
  virtual void f() = 0;
  virtual ~Base() {}
};

template <typename T>
struct Derived : Base {
  void f() { std::cout << typeid(T).name() << std::endl; }
};

int main()
{
  std::auto_ptr<Base> ptr_int(new Derived<int>);
  std::auto_ptr<Base> ptr_char(new Derived<char>);

  ptr_int->f(); //prints i with gcc
  ptr_char->f(); //prints c with gcc
}


Regards
Last edited on
@OP:
I was thinking more something like this:
(which is more or less simeonz' example, but with memory leaks)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
using namespace std;

struct Base
{
    virtual void Draw(){}
    virtual ~Base(){}
};

template <class T>
struct Derived : public Base
{
    void Draw() {cout << "drawing..." << endl;}
};

int main()
{
    Base * pb =new Derived<int>;

    pb->Draw();

    cout << "hit enter to quit...";
    cin.get(); delete pb;
    return 0;
}

But apparently you don't want to have that Draw function there...
How about something like this then?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
using namespace std;

struct Base
{
    struct BaseDrawer * drawer;
    virtual ~Base(){}
};

struct BaseDrawer
{
    virtual void Draw(Base*){}
    virtual ~BaseDrawer(){}
};

template<class T>
struct DerivedDrawer : public BaseDrawer
{
    void Draw(Base*);
};

template <class T>
struct Derived : public Base
{
    Derived() {drawer=new DerivedDrawer<T>;}
    ~Derived() {delete drawer;}
};

template <class T>
void DerivedDrawer<T>::Draw(Base*pb)
{
    Derived<T> * pd=dynamic_cast<Derived<T>*>(pb);

    cout << "drawing..." << endl;
}

int main()
{
    Base * pb =new Derived<int>;

    pb->drawer->Draw(pb);

    cout << "hit enter to quit...";
    cin.get(); delete pb;
    return 0;
}

This way, the drawing objects are separate from the ones being drawn
and you can change the way you draw an object without modifying the object itself.

Last edited on
My code has leaks, since I did not provide the empty destructor. Slaps himself on the cheek and fixes the code.
Actually, the base class is quite general in my case, it has nothing to do with images at all. That's why I did not pull up the function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Data {
    virtual void foo() {}
};

template <class Pixel, int Dim>
class Image : public Data {
public:
    itk::Image<Pixel, Dim>::Pointer image;
    void show() { ... }
};

int main() {
    Data* b = new Image<float, 3>;
    ...
    if (Image<float, 3>* d = dynamic_cast<Image<float, 3> >*>(b)) {
        d->show();
    }
    else if (Image<char, 3>* d = dynamic_cast<Image<char, 3> >*>(b)) {
        d->show();
    }
    ...
    delete b;
    return 0;
}
Last edited on
If the Data class is that general, consider puting an abstract Image class between Data and Image<Pixel,Dim>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
using namespace std;

struct Data
{
    enum {IMAGE=0, SOUND=1 /*...*/};
    int type;

    virtual ~Data(){}
};

struct BaseImage : public Data
{
    BaseImage() {type=IMAGE;}

    virtual void Show(){}
};

struct BaseSound : public Data
{
    BaseSound() {type=SOUND;}

    virtual void Play(){}
};

template<class Pixel, int Dim>
struct Image : public BaseImage
{
    void Show() {cout << "showing image..." << endl;}
};

template<int SamplingFrequency>
struct Sound : public BaseSound
{
    void Play() {cout << "playing sound..." << endl;}
};

int main()
{
    Data * pd=new Image<float,3>;
    //Data * pd=new Sound<10000>;

    switch (pd->type)
    {
        case Data::IMAGE:
        {
            BaseImage * pi=dynamic_cast<BaseImage*>(pd);
            pi->Show();
        }
        break;

        case Data::SOUND:
        {
            BaseSound * ps=dynamic_cast<BaseSound*>(pd);
            ps->Play();
        }
        break;

        //...

        default:
        cout << "type not recognized..." << endl;
    }

    cout << "hit enter to quit...";
    cin.get(); delete pd;
    return 0;
}

Last edited on
Ok. Actually, I was even considering such total pigsties as a try-catch statement using the catch blocks to infer the type. But this is actually not the point. The solution needs to scale when you start using more instantiations of the template.

To this end, this is what I could think of so far:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <typeinfo>
#include <memory>

struct Base {
  virtual ~Base() {}
};

struct IHasFunction
{
private:
  virtual void f() = 0;
protected:
  virtual ~IHasFunction() {}
  friend bool perform_f(Base *ptr);
};

template <typename T>
struct Derived : Base, public IHasFunction {
  void f() { std::cout << typeid(T).name() << std::endl; }
};

bool perform_f(Base* ptr)
{
  if(IHasFunction* has_f = dynamic_cast<IHasFunction*>(ptr))
  {
    has_f->f();
    return true;
  }
  return false;
}

int main()
{
  std::auto_ptr<Base> ptr_int(new Derived<int>);
  std::auto_ptr<Base> ptr_char(new Derived<char>);

  perform_f(ptr_int.get()); //prints i with gcc
  perform_f(ptr_char.get()); //prints c with gcc
}

The perform_f function is only proxy I use to increase the encapsulation, because I want to keep the inheritance to IHasFunction private. But that is not strictly necessary, as you may want to access IHasFunction from client code (I assumed otherwise). Also, this introduces multiple inheritance, and may incur slight penalty (I dunno). And the most derived object from Base must derive from IHasFunction only once, which is the worst nasty so far.

All of those problems can be fixed if you do something similar, but instead of using multiple inheritance, derive IHasFunction from Base and derive the generic class only from IHasFunction. Ironically, then IHasFunction will not be just interface. Also, you will need to reintroduce the parametrized constructors from Base into IHasFunction and implement them by simply redirecting them to Base.

EDIT: This latter approach is something like what m4ster r0shi meant:
If the Data class is that general, consider puting an abstract Image class between Data and Image<Pixel,Dim>

Unfortunately, templates are assumed to have nothing in common regarding their layout. So unless you inherit from some class that anchors them, you can not handle them polymorphically at run-time.

Regards
Last edited on
Yes, the solution was to create a non-template base class to which I can cast.
You helped me a lot. Thanks for you both.
Topic archived. No new replies allowed.