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