Skip to content

Instantly share code, notes, and snippets.

@hosaka
Created August 10, 2015 16:27
Show Gist options
  • Save hosaka/6e4133809c2fc8ef56e6 to your computer and use it in GitHub Desktop.
Save hosaka/6e4133809c2fc8ef56e6 to your computer and use it in GitHub Desktop.
Virtual Method Tabble, implementing dynamic binding (polymorphism) in C
#include <stdio.h>
#include <stdlib.h>
// vtable is a virtual method table that implements dynamic binding
// The goal is to write code that operates abstractly on Shapes rather than on
// specific Circles or Rects as a form of // polymorphism //
// abstract and concrete objects
typedef struct Shape Shape;
typedef struct Circle Circle;
typedef struct Rect Rect;
////////////////////////////////////////////////////////////////
// operations to perform on Shapes, note each takes a Shape * //
////////////////////////////////////////////////////////////////
// a generic draw method that does not concern with which type of shape it is
void drawPicture(Shape **begin, Shape **end);
// individual draw function that provide different implementation
void drawShape(const Shape *s)
{
printf("Not a concrete type\n");
}
void drawCircle(const Shape *s)
{
printf("Drawing circle\n");
}
void drawRect(const Shape *s)
{
printf("%p\n", s);
// how to access shape fields, to set the width/height for example?
printf("Drawing rect\n");
}
//////////////////////////////////////////////
// vtables for each type of concrete object //
//////////////////////////////////////////////
// vtable is a grouping of function pointers, this is an interface for the
// manipulations we would like to do on a Shape
typedef struct Shape_vtbl
{
void (*draw)(const Shape *s);
// any other operatins to perform on a shape
// transform
// resize
// color etc.
} Shape_vtbl;
// each vtable that points to the correct functions of a particular type
Shape_vtbl shape_vtbl = { &drawShape }; // draw method for shape
Shape_vtbl circle_vtbl = { &drawCircle };// draw circle function pointer
Shape_vtbl rect_vtbl = { &drawRect }; // draw rect
////////////////////
// abstract types //
////////////////////
// in our shape we have one element, which is a vtable
struct Shape
{
Shape_vtbl *vtbl;
// other members related to shapes, points, color
};
// other structures have super members of more generic type
struct Circle
{
Shape super; // circle is a Shape
// circle related members
};
// same with Rect
struct Rect
{
Shape super;
// others
int width;
int height;
};
//////////////////////////////
// initialisation functions //
//////////////////////////////
// the sub function call the initShape function and change the vtable
void initShape (Shape *s)
{
s->vtbl = &shape_vtbl;
}
void initCircle (Circle *c)
{
initShape( (Shape *)c ); // casting the pointer to Shape as it's the 1st member
c->super.vtbl = &circle_vtbl; // reassign the vtable to the circle vtable
// maybe other members for Circle
}
// init for rect is pretty much the same
void initRect (Rect *r, int width, int height)
{
initShape( (Shape *)r );
r->super.vtbl = &rect_vtbl;
r->width = width;
r->height = height;
}
// itarate over the Shapes array given
void drawPicture (Shape **begin, Shape **end)
{
// with each iteration we want to call the draw method that's being
// pointed to in the vtable
while (begin != end)
{
(*begin)->vtbl->draw(*begin); // access the passed in, call the draw func
// located in it's vtable
++begin; // increment the pointer
}
}
///////////////////
// usage example //
///////////////////
int main(int argc, char const *argv[])
{
// create some shapes and init them
Shape shape; initShape(&shape);
Circle circle1; initCircle(&circle1);
Circle circle2; initCircle(&circle2);
Rect rect; initRect(&rect, 10, 20);
// create an array of Shapes pointers and put the shapes there
// each item (not pointer!) has to be casted to a Shape since the array
// is of Shapes, but each element can be safely casted to Shape
Shape *shapes[4] =
{
(Shape*) &circle1,
(Shape*) &rect,
(Shape*) &shape,
(Shape*) &circle2
};
int size = sizeof(shapes)/sizeof(*shapes);
// invoke the generic picture method that takes any Shapes array
// and draws them
drawPicture(shapes, shapes + 4);
printf("%p\n", rect);
// should we want to add another type, say a triangle, our drawPicture method
// doesn't have to change at all, we would simply have to add another structs
// and the vtable
//
// unlike the switch statements, that would require the whole structure to
// change and possibly cause a mess, the dynamic binding lets you avoid that
// and separate a clean API with the implementation transparent to the caller
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment