Object-Oriented Programming (run-time polymorphism) with C99



Please find attached a copy of the "Shape class" example from
Bjarne Stroustrup, "The C++ Programming Language: Third Edition",
Chapter 2: A Tour of C++, Section 6: Object-Oriented Programming,
Subsection 2: Class Hierarchies, pages 38-40 implemented in C99.

There are four classes:

	1.) Point,
	2.) Color,
	3.) Shape and
	4.) Circle.

All classes have methods to create and destroy objects
in both automatic and free storage.
Shape has a Point and a Color.
Shape is the base class and Circle is derived from Shape.
Shape contains a pointer to a virtual function table
which is initialized to point to the virtual function table
for the derived type.
The virtual function table contains pointers
to the actual functions to be called
when the virtual function is invoked.

        > cat Point.h
        #ifndef GUARD_Point_h
        #define GUARD_Point_h 1

        typedef struct Point {
          // representation
          double        X;
          double        Y;
          } Point;
        // functions
        double
        Point_x(const Point* p);
        double
        Point_y(const Point* p);
        Point*
        Point_initialize(Point* p, double x, double y);
        // constructors
        Point
        Point_createExplicit(double x, double y);
        Point
        Point_createDefault(void);
        Point*
        Point_newExplicit(double x, double y);
        Point*
        Point_newDefault(void);
        // destructors
        void
        Point_destroy(const Point* p);
        void
        Point_delete(const Point* p);

        #endif  // GUARD_Point_h

        > cat Point.c
        // gcc -Wall -std=c99 -pedantic -I. -O2 -c Point.c

        #include<Point.h>
        #include<stdlib.h>

        // functions
        double
        Point_x(const Point* p) {
          return p->X; }
        double
        Point_y(const Point* p) {
          return p->Y; }
        Point*
        Point_initialize(Point* p, double x, double y) {
          p->X = x;
          p->Y = y;
          return p;
          }
        // constructors
        Point
        Point_createExplicit(double x, double y) {
          Point p;
          Point_initialize(&p, x, y);
          return p;
          }
        Point
        Point_createDefault(void) {
          return Point_createExplicit(0.0, 0.0); }
        Point*
        Point_newExplicit(double x, double y) {
          Point*        p = (Point*)malloc(sizeof(Point));
          Point_initialize(p, x, y);
          return p; }
        Point*
        Point_newDefault(void) {
          return Point_newExplicit(0.0, 0.0); }
        // destructors
        void
        Point_destroy(const Point* p) { }
        void
        Point_delete(const Point* p) {
          Point_destroy(p);
          free((void*)p); }

        > cat Color.h
        #ifndef GUARD_Color_h
        #define GUARD_Color_h 1
        typedef struct Color {
          // representation
          unsigned int R;       // red
          unsigned int G;       // green
          unsigned int B;       // blue
          } Color;
        // functions
        unsigned int
        Color_red(const Color *c);
        unsigned int
        Color_green(const Color *c);
        unsigned int
        Color_blue(const Color *c);
        Color*
        Color_initialize(Color* c,
            unsigned int r,
            unsigned int g,
            unsigned int b);
        // constructors
        Color
        Color_createExplicit(
            unsigned int r,
            unsigned int g,
            unsigned int b);
        Color
        Color_createDefault(void);
        Color*
        Color_newExplicit(
            unsigned int r,
            unsigned int g,
            unsigned int b);
        Color*
        Color_newDefault(void);
        // destructor
        void
        Color_destroy(const Color *c);
        void
        Color_delete(const Color *c);

        #endif  // GUARD_Color_h

        > cat Color.c
        // gcc -Wall -std=c99 -pedantic -I. -O2 -c Color.c

        #include<Color.h>
        #include<stdlib.h>

        // functions
        unsigned int
        Color_red(const Color *c) {
          return c->R; }
        unsigned int
        Color_green(const Color *c) {
          return c->G; }
        unsigned int
        Color_blue(const Color *c) {
          return c->B; }
        Color*
        Color_initialize(Color* c,
            unsigned int r,
            unsigned int g,
            unsigned int b) {
          c->R = r;
          c->G = g;
          c->B = b;
          return c;
          }
        // constructors
        Color
        Color_createExplicit(
            unsigned int r,
            unsigned int g,
            unsigned int b) {
          Color c;
          Color_initialize(&c, r, g, b);
          return c; }
        Color
        Color_createDefault(void) {
          return Color_createExplicit(0, 0, 0); }
        Color*
        Color_newExplicit(
            unsigned int r,
            unsigned int g,
            unsigned int b) {
          Color*        c = (Color*)malloc(sizeof(Color));
          Color_initialize(c, r, g, b);
          return c; }
        Color*
        Color_newDefault(void) {
          return Color_newExplicit(0, 0, 0); }
        // destructors
        void
        Color_destroy(const Color *c) { }
        void
        Color_delete(const Color *c) {
          Color_destroy(c);
          free((void*)c); }

        > cat Shape.h
        #ifndef GUARD_Shape_h
        #define GUARD_Shape_h 1

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

        typedef struct Shape {
          // representation
          const
          void* V;              // virtual function table pointer
          Point P;
          Color C;
          } Shape;
        // functions
        const Point*
        Shape_point(const Shape* s);
        const Color*
        Shape_color(const Shape* s);
        void
        actualShape_draw(const Shape* s);
        double
        actualShape_area(const Shape* s);
        void
        Shape_draw(const Shape* s);     // virtual function
        double
        Shape_area(const Shape* s);     // virtual function
        Shape*
        Shape_initialize(Shape* s,
            const  void* v,
            const Point* p,
            const Color* c);
        // constructors
        Shape
        Shape_createExplicit(
            const Point* p,
            const Color* c);
        Shape
        Shape_createDefault(void);
        Shape*
        Shape_newExplicit(
            const Point* p,
            const Color* c);
        Shape*
        Shape_newDefault(void);
        // destructors
        void
        Shape_destroy(const Shape* s);
        void
        Shape_delete(const Shape* s);

        #endif  // GUARD_Shape_h

        > cat Shape.c
        // gcc -Wall -std=c99 -pedantic -I. -O2 -c Shape.c

        #include<stdio.h>
        #include<Shape.h>
        #include<stdlib.h>

        // functions
        const Point*
        Shape_point(const Shape* s) {
          return &(s->P); }
        const Color*
        Shape_color(const Shape* s) {
          return &(s->C); }
        void
        actualShape_draw(const Shape* s) {
          fprintf(stderr, "Shape_draw(const Shape*)\n");
          fflush(stderr); }
        double
        actualShape_area(const Shape* s) {
          fprintf(stderr, "Shape_area(const Shape*)\n");
          fflush(stderr);
          return 0.0; }

        typedef struct Shape_vtable_t {
            void        (*Shape_draw)(const Shape*);
          double        (*Shape_area)(const Shape*);
          } Shape_vtable_t;
        static const
        Shape_vtable_t
        Shape_vtable = {actualShape_draw, actualShape_area};

void
Shape_draw(const Shape* s) { // virtual function
((Shape_vtable_t*)(s->V))->Shape_draw(s); }
double
Shape_area(const Shape* s) { // virtual function
return ((Shape_vtable_t*)(s->V))->Shape_area(s); }
Shape*
Shape_initialize(Shape* s,
const
void* v,
const
Point* p,
const
Color* c) {
s->V = v;
Point_initialize(&(s->P), Point_x(p), Point_y(p));
Color_initialize(&(s->C), Color_red(c), Color_green(c), Color_blue(c));
return s; }
// constructors
Shape
Shape_createExplicitShape(
const Point* p,
const Color* c) {
Shape s;
Shape_initialize(&s, (const void*)(&Shape_vtable), p, c);
return s; }
Shape
Shape_createDefault(void) {
Shape s;
Point p = Point_createDefault();
Color c = Color_createDefault();
Shape_initialize(&s, (const void*)(&Shape_vtable), &p, &c);
Color_destroy(&c);
Point_destroy(&p);
return s; }
Shape*
Shape_newExplicitShape(
const Point* p,
const Color* c) {
Shape* s = (Shape*)malloc(sizeof(Shape));
Shape_initialize(s, (const void*)(&Shape_vtable), p, c);
return s; }
Shape*
Shape_newDefault(void) {
Shape* s = (Shape*)malloc(sizeof(Shape));
Point p = Point_createDefault();
Color c = Color_createDefault();
Shape_initialize(s, (const void*)(&Shape_vtable), &p, &c);
Color_destroy(&c);
Point_destroy(&p);
return s; }
// destructors
void
Shape_destroy(const Shape* s) {
Color_destroy(Shape_color(s));
Point_destroy(Shape_point(s));
}
void
Shape_delete(const Shape* s) {
Shape_destroy(s);
free((void*)s); }


        > cat Circle.h
        #ifndef GUARD_Circle_h
        #define GUARD_Circle_h 1

        #include<Shape.h>

        typedef struct Circle {
          Shape         S;      // public base class
          double        R;      // radius
          } Circle;
        // functions
        const Shape*
        Circle_shape(const Circle* c);
        double
        Circle_radius(const Circle* c);
        void
        actualCircle_draw(const Circle* c);
        double
        actualCircle_area(const Circle* c);
        void
        Circle_draw(const Circle* c);   // virtual function
        double
        Circle_area(const Circle* c);   // virtual function
        Circle*
        Circle_initialize(Circle* c, const Shape* s, double r);
        // constructors
        Circle
        Circle_createDefault(void);
        Circle
        Circle_createExplicit(const Shape* s, double r);
        Circle*
        Circle_newDefault(void);
        Circle*
        Circle_newExplicit(const Shape* s, double r);
        // destructors
        void
        Circle_destroy(const Circle* c);
        void
        Circle_delete(const Circle* c);

        #endif  // GUARD_Circle_h

        > cat Circle.c
        // gcc -Wall -std=c99 -pedantic -I. -O2 -c Circle.c

        #include<math.h>
        #include<stdio.h>
        #include<Circle.h>
        #include<stdlib.h>

        // functions
        const Shape*
        Circle_shape(const Circle* c) {
          return &(c->S); }
        double
        Circle_radius(const Circle* c) {
          return   c->R; }
        void
        actualCircle_draw(const Circle* c) {
          fprintf(stderr, "Circle_draw(const Circle*)\n");
          fflush(stderr); }
        double
        actualCircle_area(const Circle* c) {
          const
          double      pi = 3.14159265358979323846;
          const
          double      r = Circle_radius(c);
          fprintf(stderr, "Circle_area(const Circle*)\n");
          fflush(stderr);
          return pi*r*r; }

        typedef struct Circle_vtable_t {
            void (*Circle_draw)(const Circle*);
          double (*Circle_area)(const Circle*);
          }  Circle_vtable_t;
        static const
        Circle_vtable_t
        Circle_vtable = {actualCircle_draw, actualCircle_area};
        void
        Circle_draw(const Circle* c) {  // virtual function
                 ((Circle_vtable_t*)(c->S.V))->Circle_draw(c);
          }
        double
        Circle_area(const Circle* c) {  // virtual function
          return ((Circle_vtable_t*)(c->S.V))->Circle_area(c);
          }

        Circle*
        Circle_initialize(Circle* c, const Shape* s, double r) {
          Shape_initialize(&(c->S),
              (void*)(&Circle_vtable),
              Shape_point(s),
              Shape_color(s));
          c->R = r;
          return c; }

        // constructors
        Circle
        Circle_createExplicit(const Shape* s, double r) {
          Circle      c;
          Circle_initialize(&c, s, r);
          return c; }
        Circle
        Circle_createDefault(void) {
          Circle        c;
          const
          Shape         s = Shape_createDefault();
          Circle_initialize(&c, &s, 0.0);
          Shape_destroy(&s);
          return c; }
        Circle*
        Circle_newExplicit(const Shape* s, double r) {
          Circle*       c = (Circle*)malloc(sizeof(Circle));
          Circle_initialize(c, s, r);
          return c; }
        Circle*
        Circle_newDefault(void) {
          Circle*       c = (Circle*)malloc(sizeof(Circle));
          const
          Shape         s = Shape_createDefault();
          Circle_initialize(c, &s, 0.0);
          Shape_destroy(&s);
          return c; }
        // destructors
        void
        Circle_destroy(const Circle* c) {
          Shape_destroy(Circle_shape(c));
          }
        void
        Circle_delete(const Circle* c) {
          Shape_destroy(Circle_shape(c));
          free((void*)c); }

> cat main.c
// gcc -Wall -std=c99 -pedantic -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
          Shape         s = Shape_createDefault();
          const
          Circle        c = Circle_createExplicit(&s, 2.0);
          Shape_draw((const Shape*)(&c));
          fprintf(stdout, "%g = radius\t %g = area\n",
            Circle_radius(&c), Shape_area((const Shape*)(&c)));
          return 0;
          }

        > cat Makefile
        CC=gcc
        DEFINES=
        INCLUDE=-I.
        OPTIONS=-Wall -std=c99 -pedantic -O2
        LIBRARY=
        OBJECTS=Point.o Color.o Shape.o Circle.o
        SOURCES=Point.c Color.c Shape.c Circle.c
        HEADERS=Point.h Color.h Shape.h Circle.h
        library=
        COMPILE=$(CC) $(DEFINES) $(INCLUDE) $(LIBRARY) $(OPTIONS)

        main:           $(HEADERS) $(OBJECTS) main.c
                $(COMPILE) -o main main.c $(OBJECTS) $(library)

        Point.o:        Point.h Point.c
                $(COMPILE) -c Point.c

        Color.o:        Color.h Color.c
                $(COMPILE) -c Color.c

        Shape.o:        Shape.h Shape.c
                $(COMPILE) -c Shape.c

        Circle.o:       Circle.h Circle.c
                $(COMPILE) -c Circle.c

        clean:
                rm -f main $(OBJECTS)
.



Relevant Pages

  • Re: Object-oriented programming in standard ANSI C
    ... const Point* ... typedef struct Circle { ...
    (comp.lang.c)
  • Re: new char[480000000] crashes
    ... START: class HokusVector ... Value& operator(const unsigned int& i); ... void reserve(unsigned int size); unsigned int sizeconst; bool emptyconst; void clear; ...
    (microsoft.public.vc.language)
  • Re: HashTable
    ... > void DumpKeys; ... > unsigned int EntryCount; ... * C++ code makes heavy use of the 'const' keyword in several ... class HashTable: private std::map ...
    (alt.comp.lang.learn.c-cpp)
  • Re: Help a poor FORTRAN programmer with member functions?
    ... void* should be avoided, and is ... the base class for you other shapes. ... class Square: public Shape ... Circle to give their own version of Printthat does what they need it ...
    (comp.lang.cpp)
  • Re: sharing information in a class
    ... >> is concern with circle in the Circle class. ... void Validate; ... int Validate_positive; ... float GetArea(){return area;} ...
    (comp.lang.cpp)