开发者

Matching an overloaded function to its polymorphic argument

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开发者_JS百科 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.


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.


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:

  • Double Dispatch
  • Visitor Pattern


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


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();
  }
};


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,


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;
};
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜