Object Oriented Programming in Plain Old C

It can be done! It has been done, and, quite frankly, it probably will be done better in the future. However, for now, this will illustrate how to accomplish object oriented programming in a language that is pretty much available everywhere.

I am posting the code now so that anyone who wants to view and or experiment with it can do so. I will be back occasionally to add some sort of explaination to help in the understanding of just what is going on. Please do not fault me for not providing explaination until later because I developed the code before I tried to explain it. It is just the natural order of things.

I also include the makefile that I used to compile and link all of the files. You may find it also helpful. Just put names of the object files in the object file list: [.o] and the header files in the header file list: [.h]. This file is complete as is, however.



/* 
 * File:   video.h
 * Author: dfisk
 *
 * Created on July 25, 2016, 6:14 PM
 */

#ifndef OBJ_H_INCLUDED
#define OBJ_H_INCLUDED

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

typedef void* obj;
typedef char* string;
typedef enum {false, true} bool;

typedef enum {video_, cd_, dvd_, vhs_} class_t;

/* 
 * class object 
 */

typedef struct {
    class_t class;
} object;
obj new(class_t);
void delete(obj);
class_t getClass(obj);
void setClass(obj, class_t);

#endif // OBJ_H_INCLUDED

/*============================================================================*/

#ifndef VIDEO_H_INCLUDED

#define VIDEO_H_INCLUDED

/* 
 * class video; extends object 
 */

typedef struct {
    object up;
    string title;
} video;
obj video_new();
void video_delete(obj);
string getTitle(obj);
void setTitle(obj, string);
void play(obj), video_play(obj);

/* 
 * interface ejectable 
 */

void eject(obj);

/* 
 * class cd; extends video; implements ejectable 
 */

typedef struct {
    video up;
    string dateRipped;
} cd;
obj cd_new();
void cd_delete(obj);
string getDateRipped(obj);
void setDateRipped(obj, string);
void cd_play(obj);
void cd_eject(obj);             // Implement ejectable

/* 
 * class dvd; extends cd; implements ejectable 
 */

typedef struct {
    cd up;
    bool encrypted;
} dvd;
obj dvd_new();
void dvd_delete(obj);
bool getEncrypted(obj);
void setEncrypted(obj, bool);
void dvd_play(obj);
void dvd_eject(obj);            // Implement ejectable

/* 
 * class vhs; implements ejectable 
 */

typedef struct {
    video up;
} vhs;
obj vhs_new();
void vhs_delete(obj);
void vhs_play(obj);
void vhs_eject(obj);            // Implement ejectable

#endif // VIDEO_H_INCLUDED



/*
 * File:    video.c
 * Author:  dfisk
 */

#include "video.h"

/*
 * object.h implementations 
 */

class_t getClass(obj v) {return (*(object*)v).class;}
void setClass(obj v, class_t class) {(*(object*)v).class = class;}

obj new(class_t class) {
    obj (*new[])() = {
        video_new,
        cd_new,
        dvd_new,
        vhs_new
    };
    return new[class]();
}

void delete(obj v) {
    class_t class = getClass(v);
    void (*delete[])(obj) = {
        video_delete,
        cd_delete,
        dvd_delete,
        vhs_delete
    };
    delete[class](v);
}

/*
 * virtual functions
 */

void play(obj v) {
    class_t class = getClass(v);
    void (*play[])(obj) = {
        video_play,
        cd_play,
        dvd_play,
        vhs_play
    };
    play[class](v);
}

void eject(obj v) {
    class_t class = getClass(v);
    void (*eject[])(obj) = {
        NULL,
        cd_eject,
        dvd_eject,
        vhs_eject
    };
    eject[class](v);
}

/*============================================================================*/

/*
 * video class methods
 */

string getTitle(obj v) {return ((video*)v)->title;}
void setTitle(obj v, char* t) {strcpy(((video*)v)->title, t);}

void video_play(obj v) {
    printf ("Playing %s.\n", getTitle(v));
}

/*
 * cd class methods
 */

string getDateRipped(obj v) {return ((cd*)v)->dateRipped;}
void setDateRipped(obj v, char* d) {strcpy(((cd*)v)->dateRipped, d);}


void cd_play(obj v) {
    printf("Playing %s.\n", getTitle(v));
    printf("CD was ripped on %s.", getDateRipped(v));
}

void cd_eject(obj v) {
    printf("The CD is ejected.\n");
}

/*
 * dvd class methods
 */

bool getEncrypted(obj v) {return ((dvd*)v)->encrypted;}
void setEncrypted (obj v, bool e) {((dvd*)v)->encrypted = e;}

void dvd_play(obj v) {
    printf("Playing %s.\n", getTitle(v));
    printf("This disk %s encrypted.\n", (getEncrypted(v)?"is":"is not"));
    if (!getEncrypted(v)) printf("Disk was ripped on %s.\n", getDateRipped(v));
}

void dvd_eject(obj v) {
    printf("Ejecting DVD.\n");
}

/*
 * vhs class methods
 */

void vhs_play(obj v) {
    printf("Winding tape from cartridge around play head.\n");
    printf("Playing %s.\n", getTitle(v));
}

void vhs_eject(obj v) {
    printf("Returning tape to cartridge and ejecting.\n");
}

/*============================================================================*/

/*
 * Constructor and Destructor routines
 */

obj video_new() {
    obj v = malloc(sizeof(video));
    setClass(v, video_);
    ((video*)v)->title = malloc(35);
    return v;
}

void video_delete(obj v) {
    printf("video\n");
    free(((video*)v)->title);
    free(v);
}

obj cd_new() {
    obj v = malloc(sizeof(cd));
    setClass(v, cd_);
    ((video*)v)->title = malloc(35);
    ((cd*)v)->dateRipped = malloc(11);
    return v;
}

void cd_delete(obj v) {
    printf("cd\n");
    free(((cd*)v)->dateRipped);
    video_delete(v);
}

obj dvd_new() {
    obj v = malloc(sizeof(dvd));
    setClass(v, dvd_);
    ((video*)v)->title = malloc(35);
    ((cd*)v)->dateRipped = malloc(11);
    return v;
}

void dvd_delete(obj v) {
    printf("dvd\n");
    cd_delete(v);
}

obj vhs_new() {
    obj v = malloc(sizeof(vhs));
    setClass(v, vhs_);
    ((video*)v)->title = malloc(35);
    return v;
}

void vhs_delete(obj v) {
    printf("vhs\n");
    video_delete(v);
}




/* 
 * File:   main.c
 * Author: dfisk
 *
 * Created on July 25, 2016, 6:12 PM
 */

#include "video.h"

int main(int argc, char** argv) {
    obj m1 = new(video_);
    setTitle(m1, "Our Trip to Maryland");

    obj m2 = new(cd_);
    setTitle(m2, "Santana, New");
    setDateRipped(m2, "2015/02/15");

    obj m3 = new(cd_);
    setTitle(m3, "Teenage Dream");
    setDateRipped(m3, "05/24/1999");

    obj m4 = new(dvd_);
    setTitle(m4, "The Red Skelton Hour");
    setEncrypted(m4, false);
    setDateRipped(m4, "05/26/2011");

    obj m5 = new(dvd_);
    setTitle(m5, "The Dark Knight");
    setEncrypted(m5, true);

    obj m6 = new(vhs_);
    setTitle(m6, "The Sound of Music");

    obj m7 = new(video_);
    setTitle(m7, "My Baby, Strong in the Saddle");

    /* 
     * And now for some polymorphic action 
     */
    
    obj m[] = {m1, m2, m3, m4, m5, m6, m7};
    obj e[] = {NULL, m2, m3, m4, m5, m6, NULL};
    
    for (int i=0; i<7; i++) {
        printf("-----------------\n");
        play(m[i]);
        if (e[i] == NULL) {
            printf("Video is a file. Nothing to eject.\n");
        }
        else {
            eject(e[i]);
        }
        delete (m[i]);
    }
    return 0;
}




This is an improved version from the last posting. This avoids trying to look like C++ and sticks to straight C syntax. Trying to insert functions in structures so that they can be used as object calls does not really work very well. Even C++ does not really put functions into its objects.

I will return later with some explaination on how all this works, but for now, I just want to sit back and watch it all function.