run-time polymorphism in C (long)

E

E. Robert Tisdale

The following example shows how an Object Oriented C programmer
might implement run-time polymorphism.
The Circle "class" is "derived" from the Shape "class".
Pointers to functions
which actually draw and compute the area of a shape
are stored in a "virtual function table"
and a pointer to the appropriate virtual function table
is stored in every shape object.
The "virtual" functions use this pointer to retrieve
a pointer to the "actual" function and call it
through this pointer.

$ cat Point.h
#ifndef Guard_Point_h
#define Guard_Point_h 1

typedef struct Point {
/* representation */
double X;
double Y;
} Point;
/* functions */
double
xPoint(const Point* pP);
double
yPoint(const Point* pP);
/* constructors */
Point
createDefaultPoint(void);
Point
createExplicitPoint(double x, double y);
/* destructor */
void
destroyPoint(const Point* pP);

#endif/* Guard_Point_h */

$ cat Point.c
/* gcc -I. -O2 -c Point.c
*/
#include<Point.h>

/* functions */
double
xPoint(const Point* pP) {
return pP->X; }
double
yPoint(const Point* pP) {
return pP->Y; }
/* constructors */
Point
createDefaultPoint(void) {
Point p;
p.X = 0.0;
p.Y = 0.0;
return p; }
Point
createExplicitPoint(double x, double y) {
Point p;
p.X = x;
p.Y = y;
return p;
}
/* destructor */
void
destroyPoint(const Point* pP) { }

$ cat Color.h
#ifndef Guard_Color_h
#define Guard_Color_h 1
typedef struct Color {
unsigned char R; /* red */
unsigned char G; /* green */
unsigned char B; /* blue */
} Color;
/* functions */
unsigned int
redColor(const Color* pC);
unsigned int
greenColor(const Color* pC);
unsigned int
blueColor(const Color* pC);
/* constructors */
Color
createDefaultColor(void);
Color
createExplicitColor(
unsigned int r,
unsigned int g,
unsigned int b);
/* destructor */
void
destroyColor(const Color* pC);

#endif/* Guard_Color_h */

$ cat Color.c
/* gcc -I. -O2 -c Color.c
*/
#include<Color.h>

/* functions */
unsigned int
redColor(const Color* pC) {
return pC->R; }
unsigned int
greenColor(const Color* pC) {
return pC->G; }
unsigned int
blueColor(const Color* pC) {
return pC->B; }
/* constructors */
Color
createDefaultColor(void) {
Color c;
c.R = 0;
c.G = 0;
c.B = 0;
return c; }
Color
createExplicitColor(
unsigned int r,
unsigned int g,
unsigned int b) {
Color c;
c.R = r;
c.G = g;
c.B = b;
return c; }
/* destructor */
void
destroyColor(const Color* pC) { }

$ cat Shape.h
#ifndef Guard_Shape_h
#define Guard_Shape_h 1

#include<Point.h>
#include<Color.h>

typedef struct Shape {
const
void* pV; /* virtual function table */
Point P; /* first public base class */
Color C; /* second public base class */
} Shape;
/* functions */
Point*
pointShape(Shape* pS);
Color*
colorShape(Shape* pS);
void
drawShape(const Shape* pS);
void
actualDrawShape(const Shape* pS);
double
areaShape(const Shape* pS);
double
actualAreaShape(const Shape* pS);
/* constructors */
Shape
createDefaultShape(void);
Shape
createExplicitShape(
const Point* pP,
const Color* pC);
/* destructor */
void
destroyShape(const Shape* pS);

#endif/* Guard_Shape_h */

$ cat Shape.c
/* gcc -I. -O2 -c Shape.c
*/
#include<stdio.h>
#include<Shape.h>

/* functions */
Point*
pointShape(Shape* pS) {
return &(pS->P); }
Color*
colorShape(Shape* pS) {
return &(pS->C); }
void
actualDrawShape(const Shape* pS) {
fprintf(stderr, "drawShape(const Shape*)\n");
fflush(stderr); }
double
actualAreaShape(const Shape* pS) {
fprintf(stderr, "areaShape(const Shape*)\n");
fflush(stderr);
return 0.0; }

typedef struct vTableShape_t {
void (*drawShape)(const Shape*);
double (*areaShape)(const Shape*);
} vTableShape_t;
static const vTableShape_t
vTableShape = {actualDrawShape, actualAreaShape};
void
drawShape(const Shape* pS) {
((vTableShape_t*)(pS->pV))->drawShape(pS); }
double
areaShape(const Shape* pS) {
return ((vTableShape_t*)(pS->pV))->areaShape(pS); }
/* initializers */
Shape* initializeDefaultShape(Shape* pS, const void* pV) {
pS->pV = pV;
pS->P = createDefaultPoint();
pS->C = createDefaultColor();
return pS; }
Shape* initializeExplicitShape(Shape* pS, const void* pV,
const Point* pP, const Color* pC) {
pS->pV = pV;
pS->P = *pP;
pS->C = *pC;
return pS; }
/* constructors */
Shape
createDefaultShape(void) {
Shape S;
initializeDefaultShape(&S, &vTableShape);
return S; }
Shape
createExplicitShape(
const Point* pP,
const Color* pC) {
Shape S;
return *initializeExplicitShape(&S, &vTableShape, pP, pC); }
/* destructor */
void
destroyShape(const Shape* pS) {
destroyColor(colorShape((Shape*)pS));
destroyPoint(pointShape((Shape*)pS)); }

$ cat Circle.h
#ifndef Guard_Circle_h
#define Guard_Circle_h 1

#include<Shape.h>

typedef struct {
Shape S; /* public base class */
double R; /* radius */
} Circle;
/* functions */
Shape*
shapeCircle(Circle* pC);
double
radiusCircle(const Circle* pC);
void
drawCircle(const Circle* pC);
void
actualDrawCircle(const Circle* pC);
double
areaCircle(const Circle* pC);
double
actualAreaCircle(const Circle* pC);
/* constructors */
Circle
createDefaultCircle(void);
Circle
createExplicitCircle(const Point* pP,
const Color* pC, double r);
/* destructor */
void
destroyCircle(const Circle* pC);

#endif/* Guard_Circle_h */

$ cat Circle.c
/* gcc -I. -O2 -c Circle.c
*/
#include<math.h>
#include<stdio.h>
#include<Circle.h>

/* functions */
Shape*
shapeCircle(Circle* pC) {
return &(pC->S); }
double
radiusCircle(const Circle* pC) {
return pC->R; }
void
actualDrawCircle(const Circle* pC) {
fprintf(stderr, "actualDrawCircle(const Circle*)\n");
fflush(stderr); }
double
actualAreaCircle(const Circle* pC) {
const
double pi = 3.14159265358979323846;
double r = radiusCircle(pC);
fprintf(stderr, "actualAreaCircle(const Circle*)\n");
fflush(stderr);
return pi*r*r; }

typedef struct vTableCircle_t {
void (*drawCircle)(const Circle*);
double (*areaCircle)(const Circle*);
} vTableCircle_t;
static const vTableCircle_t
vTableCircle = {actualDrawCircle, actualAreaCircle};
void
drawCircle(const Circle* pC) {
((vTableCircle_t*)(pC->S.pV))->drawCircle(pC); }
double
areaCircle(const Circle* pC) {
return ((vTableCircle_t*)(pC->S.pV))->areaCircle(pC); }
/* constructors */
Circle
createDefaultCircle(void) {
Circle C;
extern Shape* initializeDefaultShape(Shape*, const void*);
initializeDefaultShape(&(C.S), &vTableCircle);
C.R = 0.0;
return C; }
Circle
createExplicitCircle(const Point* pP,
const Color* pC, double r) {
Circle C;
extern Shape* initializeExplicitShape(Shape*, const void*,
const Point*, const Color*);
initializeExplicitShape(&(C.S), &vTableCircle, pP, pC);
C.R = r;
return C; }
/* destructor */
void
destroyCircle(const Circle* pC) {
destroyShape(shapeCircle((Circle*)pC)); }

$ cat main.c
/* gcc -I. -O2 -o main main.c Circle.o Shape.o Color.o Point.o
*/
#include<stdio.h>
#include<Circle.h>

int
main(int argc, char* argv[]) {
const Point point = createDefaultPoint();
const Color color = createDefaultColor();
const Circle circle = createExplicitCircle(
&point, &color, 2.0);
drawShape((Shape*)(&circle));
fprintf(stdout, "%g = radius\t %g = area\n",
radiusCircle(&circle), areaShape((Shape*)(&circle)));
destroyCircle(&circle);
destroyColor(&color);
destroyPoint(&point);
return 0;
}
 
C

Case

E. Robert Tisdale said:
The following example shows how an Object Oriented C programmer
might implement run-time polymorphism.
The Circle "class" is "derived" from the Shape "class".
Pointers to functions
which actually draw and compute the area of a shape
are stored in a "virtual function table"
and a pointer to the appropriate virtual function table
is stored in every shape object.
The "virtual" functions use this pointer to retrieve
a pointer to the "actual" function and call it
through this pointer.
....snip...

Seems interesting. But, could you please indent the code
'properly' and please use vertical white space. This code
hurts my eyes and is very frustrating because the common
visual clues are missing: It is unclear where definitions
and/or function calls begin and/or end. Have a look at K&R
for a 'good' example of 'proper' indentation. (At least
place } at a line of there own, I would say.)

Kees
 
T

those who know me have no need of my name

in comp.lang.c i read:
Pointers to functions which actually draw and compute the area of a shape
are stored in a "virtual function table" and a pointer to the appropriate
virtual function table is stored in every shape object.
const
void* pV; /* virtual function table */

standard c does not require an implementation to meaningfully handle
conversion between a function pointer and an object pointer.
 
E

E. Robert Tisdale

Something that calls itself
those said:
standard c does not require an implementation to meaningfully handle
conversion between a function pointer and an object pointer.

What would you suggest instead?
 
C

Chris Torek

in comp.lang.c i read:

standard c does not require an implementation to meaningfully handle
conversion between a function pointer and an object pointer.

In general, vtables are data objects, and the description (quoted
in ">>" above) sounds as though this is the case here. The
"void *" in pV would presumably point to the data object, which
in turn has pointers to functions.

Of course, when one is "manually expanding" such things in C, there
is no reason not to use a pointer to the structure containing the
(named) operations:

struct zorg_ops {
void (*increase_evilness)(struct zorg *, double howmuch);
void (*absorb_missles)(struct zorg *, int nmissiles);
/* ... */
};

struct zorg {
/* any base class goes here */
struct zorg_ops *ops;
double degree_of_evil;
/* ... more data members as needed */
};

Then when one has a "struct zorg *" and wants it to absorb three
missiles:

struct zorg *zp;
...
zp->ops->absorb_missiles(zp, 3);

In the usual case, the "ops" would actually be part of the base
class, so this:

zp->base.ops->absorb(zp, 3);

might be a bit more likely.
 
T

those who know me have no need of my name

in comp.lang.c i read:
Something that calls itself
those who know me have no need of my name wrote:
Pointers to functions [...] are stored in a "virtual function table"
standard c does not require an implementation to meaningfully handle
conversion between a function pointer and an object pointer.

What would you suggest instead?

i don't have a suggestion for you. i just wanted to point out that this
technique isn't highly portable.
 
E

E. Robert Tisdale

Something said:
in comp.lang.c i read:
Something that calls itself
those who know me have no need of my name wrote:
Pointers to functions [...] are stored in a "virtual function table"

standard c does not require an implementation to meaningfully handle
conversion between a function pointer and an object pointer.

What would you suggest instead?


i don't have a suggestion for you.
i just wanted to point out that this technique isn't highly portable.

You are confused. There is *no*
"conversion between a function pointer and an object pointer"
in my example.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top