You would use polymorphism.
- Make a pure virtual function in your base class (i.e. when declaring the function assign it to 0 as in
void DrawShape() = 0;
) - Declare and define that function in your derived classes.
That way you can just call DrawShape()
on each of these objects even if it is passed as a Shape object.
Alternatives (NOTE: code has not been tested):
Function pointer, which is like building your own vtable aka delegate.
struct square { void (*draw)(square&); }; void drawSquare(square& obj) { // draw square code // there is no 'this'. must access members via `obj`. } square s; s.draw = drawSquare; s.draw(s);
Functor, which is a class that overrides operator() and also is like a delegate
struct square { // Note that std::function can hold a function pointer as well as a functor. function<void(square&)> draw; }; struct drawSquare { void oparator()(square& obj) { // draw square code // there is no 'this'. must access members via `obj`. } }; square s; square s.draw = drawSquare(); s.draw(s);
NOTE: 1 and 2 can also be initialised with lambda functions:
square s; s.draw = [](square& obj) { // draw square code // there is no 'this'. must access members via `obj`. }; s.draw(s);
NOTE: 1 could be done with a template:
struct square; template <void (*DRAW)(square&)> struct square { void draw() { DRAW(*this); } }; void drawSquare(square& obj) { // draw square code // there is no 'this'. must access members via `obj`. } square s<&drawSquare>; s.draw();
NOTE: 2 could be done with a template as well:
template <typename DRAW> struct square { void draw() { // First set of parentheses instantiate the DRAW object. // The second calls the functor. DRAW()(*this); } }; struct drawSquare { void oparator()(square& obj) { // draw square code // there is no 'this'. must access members via `obj`. } }; square s<drawSquare>; s.draw();
Or alternatively, which would allow the passing of a stateful functor:
template <typename DRAW> struct square { DRAW draw; }; struct drawSquare { void operator()(square& obj) { // draw square code // there is no 'this'. must access members via `obj`. } }; square s<drawSquare>; s.draw = drawSquare(); s.draw(s);
Inherit from another class that implements the function you want either with a templated base class (IIRC, this was done in the ATL). This is just rolling your own hard-coded vtable and is called the Curiously Recurring Type Pattern (CRTP).
template <class D> struct shape { inline void draw() { return static_cast<D&>(*this).draw(); } }; void draw(square& obj) { // draw square code // No 'this' available. must access shape members via `obj`. } struct square : public D<square> { void draw() { drawSquare(*this); } };
Have your
draw
class inherit from thetype of shape
class which inherits from the baseshape
class.struct shape { virtual void draw() = 0; }; struct square : public shape { }; struct drawSquare : public square { virtual void draw() { // draw square code // you access the square's public or protected members from here } };
Use a
std::unordered_map
#include <unordered_map> #include <typeinfo> #include <functional> struct shape { }; struct square : public shape { }; void drawSquare(shape& o) { // this will throw an exception if dynamic cast fails, but should // never fail if called from function void draw(shape& obj). square& obj = dynamic_cast<square&>(o); // draw square code // must access shape members via `obj`. } std::unordered_map<size_t, std::function<void(shape&)>> draw_map { { type_id(square).hash(), drawSquare } }; void draw(shape& obj) { // This requires the RTTI (Run-time type information) to be available. auto it = draw_map.find(type_id(obj).hash()); if (it == draw_map.end()) throw std::exception(); // throw some exception (*it)(obj); }
NOTE: if you are using g++ 4.7, be warned
unordered_map
has been shown to have performance issues.