19

Okay, the title is a mouthful and I think that's probably why it has been tough to find an answer through google or this site. It might just be that I don't know how to express the problem correctly but here goes:

I have a series of methods in a SimpleOpenGLRenderer class that all take a single argument that extends the Model class. So the idea is that depending on the type of model, the renderer will invoke the correct method that knows how to render it. Here is a simplified executable example based on the problem:

#include <stdio.h>

class Model {};

class Cube : public Model {};

class Sphere : public Model {};

class Renderer
{
  public:
    virtual void renderModel(const Model& model) = 0;
};

class SimpleOpenGLRenderer
{
  public:
    void renderModel(const Cube& model)
    {
      printf("Render the cube.\n");
    }

    void renderModel(const Model& model)
    {
      printf("Throw an exception, my renderer does not support the model type you have provided.\n");
    }

    void renderModel(const Sphere& model)
    {
      printf("Render the sphere.\n");
    }
};

int
main(int argc, char** argv)
{
  Cube cube;
  Model& model = cube;
  SimpleOpenGLRenderer renderer;

  renderer.renderModel(cube);
  renderer.renderModel(model);
}

The output from the example is:

Render the cube.
Throw an exception, my renderer does not support the model type you have provided.

It may seem obvious to a more seasoned C++ developer that this does not work as planned but it just doesn't make sense to me. At runtime I will not know the exact type of the Model passed to the renderer (hence the attempted overloading to resolve it). Coming from a Java background, I have used this technique before and in Java the method called will be that which best matches the runtime type of the argument. In C++ it seems to match to the compile-time type of the reference, even if that reference may end up being to a subclass that - to my mind - better matches another function.

Up until now I had taken this runtime type matching for granted. Does it simply not exist in C++ or am I going about this the wrong way? Should I be doing something differently in C++ to achieve it?

Thanks,

Gary.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
Gyan aka Gary Buyn
  • 12,242
  • 2
  • 23
  • 26

6 Answers6

18

Overloads in C++ are resolved at compile time, based on the static type of the argument.

There's a technique known as "double-dispatch" that might be of use:

class Model {
    virtual void dispatchRender(Renderer &r) const = 0;
};

class Cube : public Model {
    virtual void dispatchRender(Renderer &r) const {
        r.renderModel(*this); // type of "this" is const Cube*
};

int main() {
    Cube cube;
    Model &model = cube;
    SimpleOpenGLRenderer renderer;
    cube.dispatchRender(renderer);
}

Note that the Renderer base class needs to contain all the overloads that SimpleOpenGLRenderer currently does. If you want it to be specific to SimpleOpenGLRenderer what overloads exist then you could put a Simple-specific dispatch function in Model, or you could ignore this technique and instead use dynamic_cast repeatedly in SimpleOpenGLRenderer::renderModel to test the type.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 2
    Thanks. I haven't used that pattern before. I guess it might not be very common in Java. I've done some testing and I think I probably will fall back to the `dynamic_cast` solution. I was hoping for something more elegant but the double-dispatch/visitor pattern seems to introduce all sorts of dependencies and tight coupling that I'm not too keen on. – Gyan aka Gary Buyn Aug 02 '11 at 10:06
2

In your code, the function overloads are resolved based on the static type of the argument.

What you need probably is double-dispatch mechanism which is very close to Visitor pattern. Read these:

Nawaz
  • 353,942
  • 115
  • 666
  • 851
1

For "runtime overloading" based on the dynamic type it is possible to use visitor pattern.

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

Your code is a good candidate of runtime type matching, if you use it. Here you are receiving Cube into Model& and passing the same simply to the renderModel(). Till now you haven't given chance to the compiler to use the runtime type. But rather relying on the static type of the object.

In 2 ways you could have used the runtime type checking. One is using dynamic_cast<> and other one is providing the interface method in Model. i.e.

class Model {
  virtual void print () { printf("throw..."); }  // provide an interface method
};

class Cube : public Model {
  virtual void print () { print("Render cube\n"; }  // implement it
};

class Sphere : public Model {
  virtual void print () { print("Render sphere\n"; }  // implement it
};

class SimpleOpenGLRenderer
{
  public:
  void renderModel(const Model& model)
  {
    model.print();
  }
};
iammilind
  • 68,093
  • 33
  • 169
  • 336
  • I think I will use the `dynamic_cast` solution for lack of anything better considering static type resolution. I don't want the `Model`s to be responsible for rendering because I have like 10 different `Renderer`s already that perform the rendering in different ways. – Gyan aka Gary Buyn Aug 02 '11 at 10:09
0

In C++ the resolution of which overload to call, is done at compile time.

To make the effective implementation depend on the polymorphic argument type, you need to consult that argument, i.e., call a virtual method on the argument.

I think the cleanest way to do that here is what's called the visitor pattern. Your SimpleOpenGLRenderer can call a method model.renderOn( *this ). Then Model::renderOn is either a set of overloads, one for each possible type of renderer, or it is a single virtual method that uses dynamic_cast to discover the type of renderer. In any way, it then calls back on the renderer, but now that call knows what type of renderer it is and what type itself is, and can also call a very specific rendering method, like, SimpleOpenGLRenderer::renderCube.

Cheers,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

The other solutions here will do exactly what you want. But in my opinion at the cost of complexity. If your problem is exactly as described, I would suggest to change the architecture of your solution instead. Aren't the renderer trying to do the job of the model? What I see is a kind a overload generated switch sentence.

How about making the models rendering themself, perhaps by using some class offering more primitive drawing methods:

class Cube : public Model {
  render(RenderTool& tool) {
    tool.renderCube(); //or even more low level
  }
};

class SimpleOpenGLRenderer {
  public:
  RenderModel(Model& model) {
    model.render(renderTool);
  }
  private:
  SomeOpenGLRenderingTool renderTool;
};
daramarak
  • 6,115
  • 1
  • 31
  • 50
  • I don't see that this is any simpler than the other methods presented. If I'm using the 'tool' to do the rendering then what is the 'renderer' for? It feels like an unwanted middle man. I don't want the `Model` to be responsible for rendering. Rendering may be accomplished in multiple different ways and even using different graphics APIs. The `Model`'s responsibility is simply to describe the geometry. – Gyan aka Gary Buyn Aug 02 '11 at 10:11
  • I try to adhere to the information expert principle. In this case the Model is the only one who knows how the shape is. Switching on type to make an external object do the drawing is IMO a needless breach of encapsulation. What the renderer is for is a valid question. IMO perhaps the model should use a renderer not vice versa. – daramarak Aug 02 '11 at 10:25