{ |one, step, back| }

The Shape Example in ANSI C

Contributed by Jim Weirich

Code for ANSI C

File: shapes.c

/* 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;
}

Output

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