Contributed by Jim Weirich
/* Object Orientation in C */ #include <stdio.h> #include <stdlib.h> /* abstract interface declaration */ struct Shape { struct ShapeFuncTable *funcTable; void *privateData; }; struct ShapeFuncTable { void (*Draw)(struct Shape * obj); void (*MoveTo)(struct Shape * obj, int newx, int newy); void (*RMoveTo)(struct Shape * obj, int dx, int dy); }; /* Class Rectangle */ struct RectanglePrivateData { int x, y; int width; int height; }; void RectangleDraw (struct Shape * obj) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; printf ("Drawing a Rectangle at (%d,%d), width %d, height %d\n", rdata->x, rdata->y, rdata->width, rdata->height); } void RectangleMoveTo (struct Shape * obj, int newx, int newy) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->x = newx; rdata->y = newy; } void RectangleRMoveTo (struct Shape * obj, int dx, int dy) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->x += dx; rdata->y += dy; } void RectangleSetWidth (struct Shape * obj, int newWidth) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->width = newWidth; } void RectangleSetHeight (struct Shape * obj, int newHeight) { struct RectanglePrivateData * rdata = (struct RectanglePrivateData*)obj->privateData; rdata->height = newHeight; } struct RectangleFuncTable { void (*Draw)(struct Shape * obj); void (*MoveTo)(struct Shape * obj, int newx, int newy); void (*RMoveTo)(struct Shape * obj, int dx, int dy); void (*SetWidth)(struct Shape * obj, int width); void (*SetHeight)(struct Shape * obj, int height); } rectangleFuncTable = { RectangleDraw, RectangleMoveTo, RectangleRMoveTo, RectangleSetWidth, RectangleSetHeight }; struct Shape * MakeRectangle ( int initx, int inity, int initw, int inith) { struct Shape * obj = malloc (sizeof *obj); struct RectanglePrivateData * rdata = malloc (sizeof *rdata); obj->funcTable = (struct ShapeFuncTable*) &rectangleFuncTable; obj->privateData = rdata; rdata->x = initx; rdata->y = inity; rdata->width = initw; rdata->height = inith; return obj; } /* Class Circle */ struct CirclePrivateData { int x, y; int radius; }; void CircleDraw (struct Shape * obj) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; printf ("Drawing a Circle at (%d,%d), radius %d\n", cdata->x, cdata->y, cdata->radius); } void CircleMoveTo (struct Shape * obj, int newx, int newy) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; cdata->x = newx; cdata->y = newy; } void CircleRMoveTo (struct Shape * obj, int dx, int dy) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; cdata->x += dx; cdata->y += dy; } void CircleSetRadius (struct Shape * obj, int newRadius) { struct CirclePrivateData * cdata = (struct CirclePrivateData*)obj->privateData; cdata->radius = newRadius; } struct CircleFuncTable { void (*Draw)(struct Shape * obj); void (*MoveTo)(struct Shape * obj, int newx, int newy); void (*RMoveTo)(struct Shape * obj, int dx, int dy); void (*SetRadius)(struct Shape * obj, int width); } circleFuncTable = { CircleDraw, CircleMoveTo, CircleRMoveTo, CircleSetRadius }; struct Shape * MakeCircle (int initx, int inity, int initr) { struct Shape * obj = malloc (sizeof *obj); struct CirclePrivateData * cdata = malloc (sizeof *cdata); obj->funcTable = (struct ShapeFuncTable*) &circleFuncTable; obj->privateData = cdata; cdata->x = initx; cdata->y = inity; cdata->radius = initr; return obj; } /* =================================================================== * DoSomethingWithShape is a fuction that takes a polymorphic shape * and manipulates it according to its interface. It doesn't care if * shape is a circle or a rectangle, as long as it conforms to the * shape interface. * * If we would pull the shape interface into a separate header file * and move this function into its own ".c" file (that referenced the * Shape only definitions), then we could add new shapes without * changing a single line of source code in this function, yet the * function would still handle shapes that were defined by the user. */ void DoSomethingWithShape (struct Shape * s) { s->funcTable->Draw (s); s->funcTable->RMoveTo (s, 100, 100); s->funcTable->Draw (s); } /* =================================================================== * Main Program */ int main () { int i; struct Shape * shapes[2]; struct Shape * rect; /* using shapes polymorphically */ shapes[0] = MakeRectangle (10, 20, 5, 6); shapes[1] = MakeCircle (15, 25, 8); for (i=0; i<2; ++i) { DoSomethingWithShape (shapes[i]); } /* access a rectangle specific function */ rect = MakeRectangle (0, 0, 15, 15); ((struct RectangleFuncTable*)rect->funcTable)->SetWidth(rect, 30); rect->funcTable->Draw (rect); return 0; }
Drawing a Rectangle at (10,20), width 5, height 6 Drawing a Rectangle at (110,120), width 5, height 6 Drawing a Circle at (15,25), radius 8 Drawing a Circle at (115,125), radius 8 Drawing a Rectangle at (0,0), width 30, height 15