Skip to content

Instantly share code, notes, and snippets.

@xianbaum
Last active December 8, 2017 02:37
Show Gist options
  • Save xianbaum/463f13a8d39014131b3f4fa67a241119 to your computer and use it in GitHub Desktop.
Save xianbaum/463f13a8d39014131b3f4fa67a241119 to your computer and use it in GitHub Desktop.
An implementation of multiple inheritence using class-based inheritence patterns and interface/virtual class based inheritence using standard ANSI C89 to use for future reference
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
/*
helloable.c
THIS SOURCE FILE IS IN THE PUBLIC DOMAIN
Notes on this implementation:
1. Base structs are pretty straightforward, but basically, they are
implemented as follows: the base struct must be the very first
element in the class. That way, the pointer can be cast to and from
the base and derived struct. Alternatively, the base can be accessed
just by calling the "base" parameter (which is what the base struct
should probably be named). You can cast to and from any base or derived
struct. Multiple base/derived inheritence was NOT attempted, and the
design would have to be different if it was. This kind of MI is gross!
2. Based on how the ToHelloable and FromHelloable macros are implemented,
every struct that implements it must have a pointer to the vtable, and
it must be named helloable. This is because the macros rely on the struct
having a member named "helloable".
3. Related to #2: If a base struct implements an interface, and the
derived struct does not, the derived struct must be cast to that base
struct, or else the macro won't work. You can also just pass the "base"
member which produces the same end result. Similarly, in this case, the
FromHelloable macro must have the base struct typename as the first
argument before it can be cast to the derived struct. Then the result
can be cast to the derived struct. This is an inconvenience.
4. Since each interface uses only one pointer to determine two pieces of
information, the macros must pass a pointer to the struct member. The
ToHelloable macro creates a pointer to the struct member. This is then
dereferenced to get the pointer to the vtable, and then the method is
called from there. if the "this" pointer is required, the non-dereferenced
pointer is passed as the first parameter. Unless just more interface
methods are required, you probably also need the base struct. That's where
the FromHelloable macro comes in handy. The 1st line of each method calls
FromHelloable with the struct type name that implemend it to get base
pointer by offsetting the helloable struct member pointer. That's why
ToHelloable needs to produce a pointer to the struct member.
*/
struct Helloable{
void (*SayHello)(void);
void (*SayGoodbye)(struct Helloable const **helloable, char *name);
};
typedef struct Helloable const Helloable;
/*This needs to be a pointer to the struct member containing the pointer to a
vtable, not a pointer to the vtable! We need it to be a pointer to the struct
member because we are using it to get both the vtable and the struct*/
#define ToHelloable(casting) &(casting->helloable)
#define FromHelloable(type, helloable) \
((type*)((char*)helloable - offsetof(type, helloable)))
typedef struct
{
int weight;
char *name;
int hunger_level;
} Being;
void Being_PrintStats(Being *being)
{
printf("Name\t\t\t%s\nWeight\t\t\t%d lbs\nHunger level\t\t%d\n", being->name,
being->weight, being->hunger_level);
}
typedef struct
{
Being base;
char *breed;
} Animal;
void Animal_PrintStats(Animal *animal)
{
Being_PrintStats((Being *)animal);
printf("Breed\t\t\t%s\n", animal->breed);
}
typedef struct
{
Animal base;
int barks_per_minute;
Helloable *helloable;
} Dog;
void Dog_PrintStats(Dog *dog)
{
Animal_PrintStats((Animal *)dog);
printf("Barks per min\t\t%d\n", dog->barks_per_minute);
}
void Dog_SayHello(void)
{
printf("Woof!\n");
}
void Dog_SayGoodbye(Helloable **helloable, char *name)
{
Being *self = (Being*)FromHelloable(Dog, helloable);
printf("Woof-woof, from %s to %s\n", self->name, name);
}
Helloable DogHelloableVtable = { Dog_SayHello, Dog_SayGoodbye };
Dog *Dog_Create()
{
Dog *dog = malloc(sizeof(Dog));
dog->helloable = &(DogHelloableVtable);
return dog;
}
typedef struct
{
Animal base;
double purr_frequency;
Helloable *helloable;
} Cat;
void Cat_PrintStats(Cat *cat)
{
Animal_PrintStats((Animal *)cat);
printf("Purr frequency\t\t%.2f Hz\n", cat->purr_frequency);
}
void Cat_SayHello(void)
{
printf("Meow!\n");
}
void Cat_SayGoodbye(Helloable **helloable, char *name)
{
Being *self = (Being*)FromHelloable(Cat, helloable);
printf("Meow-meow, from %s to %s\n", self->name, name);
}
Helloable CatHelloableVtable = { Cat_SayHello, Cat_SayGoodbye };
Cat *Cat_Create()
{
Cat *cat = malloc(sizeof(Cat));
cat->helloable = &(CatHelloableVtable);
return cat;
}
typedef struct
{
Being base;
char *occupation;
Helloable *helloable;
} Person;
void Person_PrintStats(Person *person)
{
Being_PrintStats((Being *)person);
printf("Occupation\t\t%s\n", person->occupation);
}
void Person_SayHello(void)
{
printf("Hello, world!\n");
}
void Person_SayGoodbye(Helloable **helloable, char *name)
{
Being *self = (Being*)FromHelloable(Person, helloable);
printf("Goodbye, from %s to %s\n", self->name, name);
}
Helloable PersonHelloableVtable = { Person_SayHello, Person_SayGoodbye };
Person *Person_Create()
{
Person *person = malloc(sizeof(Person));
person->helloable = &(PersonHelloableVtable);
return person;
}
int main(void)
{
Dog *dog = Dog_Create();
Cat *cat = Cat_Create();
Person *person = Person_Create();
Helloable **my_helloable_array[3];
int i;
dog->base.base.weight = 10;
dog->base.base.hunger_level = 5;
dog->base.base.name = "Libby";
dog->base.breed = "lab";
dog->barks_per_minute = 4;
cat->base.base.weight = 3;
cat->base.base.name = "Kitty";
cat->base.base.hunger_level = 0;
cat->base.breed = "tabby";
cat->purr_frequency = 21.98;
person->base.weight = 195;
cat->base.base.hunger_level = 1;
person->base.name = "Christian";
person->base.hunger_level = 1;
person->occupation = "Programmer";
my_helloable_array[0] = ToHelloable(dog);
my_helloable_array[1] = ToHelloable(cat);
my_helloable_array[2] = ToHelloable(person);
Dog_PrintStats(dog);
Cat_PrintStats(cat);
Person_PrintStats(person);
for (i = 0; i < 3; i++)
{
Helloable **helloable = my_helloable_array[i];
(*helloable)->SayHello();
(*helloable)->SayGoodbye(helloable, "Jack");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment