Skip to content

Instantly share code, notes, and snippets.

@cwchentw
Last active May 30, 2022 23:35
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cwchentw/42780178b3948c1dd94454e934fc05f3 to your computer and use it in GitHub Desktop.
Save cwchentw/42780178b3948c1dd94454e934fc05f3 to your computer and use it in GitHub Desktop.
Polymorphism with Union in C (Apache 2.0)
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include "animal.h"
#include "dog.h"
#include "duck.h"
#include "tiger.h"
struct animal {
Animal_t type;
union {
Dog *dog;
Duck *duck;
Tiger *tiger;
} _animal;
};
Animal * animal_new(Animal_t t, char *name)
{
Animal *a = malloc(sizeof(Animal));
if (!a) {
perror("Unable to allocate animal a");
return a;
}
switch (t) {
case ANIMAL_TYPE_DOG:
a->type = ANIMAL_TYPE_DOG;
a->_animal.dog = dog_new(name);
if (!(a->_animal.dog)) {
perror("Unable to allocate dog");
goto ANIMAL_FREE;
}
break;
case ANIMAL_TYPE_DUCK:
a->type = ANIMAL_TYPE_DUCK;
a->_animal.duck = duck_new(name);
if (!(a->_animal.duck)) {
perror("Unable to allocate duck");
goto ANIMAL_FREE;
}
break;
case ANIMAL_TYPE_TIGER:
a->type = ANIMAL_TYPE_TIGER;
a->_animal.tiger = tiger_new(name);
if (!(a->_animal.tiger)) {
perror("Unable to allocate tiger");
goto ANIMAL_FREE;
}
break;
default:
assert("Invalid animal" && false);
}
return a;
ANIMAL_FREE:
free(a);
a = NULL;
return a;
}
char * animal_name(Animal *self)
{
assert(self);
switch (self->type) {
case ANIMAL_TYPE_DOG:
return dog_name(self->_animal.dog);
case ANIMAL_TYPE_DUCK:
return duck_name(self->_animal.duck);
case ANIMAL_TYPE_TIGER:
return tiger_name(self->_animal.tiger);
default:
assert("Invalid animal" && false);
}
}
char * animal_speak(Animal *self)
{
assert(self);
switch (self->type) {
case ANIMAL_TYPE_DOG:
return dog_speak(self->_animal.dog);
case ANIMAL_TYPE_DUCK:
return duck_speak(self->_animal.duck);
case ANIMAL_TYPE_TIGER:
return tiger_speak(self->_animal.tiger);
default:
assert("Invalid animal" && false);
}
}
void * animal_raw(Animal *self)
{
assert(self);
switch (self->type) {
case ANIMAL_TYPE_DOG:
return (void *) self->_animal.dog;
case ANIMAL_TYPE_DUCK:
return (void *) self->_animal.duck;
case ANIMAL_TYPE_TIGER:
return (void *) self->_animal.tiger;
default:
assert("Invalid animal" && false);
}
}
void animal_free(void *self)
{
if (!self) {
return;
}
switch (((Animal *) self)->type) {
case ANIMAL_TYPE_DOG:
dog_free(((Animal *) self)->_animal.dog);
break;
case ANIMAL_TYPE_DUCK:
duck_free(((Animal *) self)->_animal.duck);
break;
case ANIMAL_TYPE_TIGER:
tiger_free(((Animal *) self)->_animal.tiger);
break;
default:
assert("Invalid animal" && false);
}
free(self);
}
#ifndef ANIMAL_H
#define ANIMAL_H
typedef enum {
ANIMAL_TYPE_DUCK,
ANIMAL_TYPE_DOG,
ANIMAL_TYPE_TIGER
} Animal_t;
typedef struct animal Animal;
Animal * animal_new(Animal_t t, char *name);
char * animal_name(Animal *self);
char * animal_speak(Animal *self);
void * animal_raw(Animal *self);
void animal_free(void *self);
#endif // ANIMAL_H
#include <assert.h>
#include <stdlib.h>
#include "dog.h"
struct dog {
char *name;
};
Dog * dog_new(char *name)
{
Dog *dog = malloc(sizeof(Dog));
if (!dog) {
return dog;
}
dog->name = name;
return dog;
}
char * dog_name(Dog *self)
{
assert(self);
return self->name;
}
char * dog_speak(Dog *self)
{
assert(self);
return "Wow wow";
}
void dog_free(void *self)
{
if (!self) {
return;
}
free(self);
}
#ifndef DOG_H
#define DOG_H
typedef struct dog Dog;
Dog * dog_new(char *name);
char * dog_name(Dog *self);
char * dog_speak(Dog *self);
void dog_free(void *self);
#endif // DOG_H
#include <assert.h>
#include <stdlib.h>
#include "duck.h"
struct duck {
char *name;
};
Duck * duck_new(char *name)
{
Duck *duck = malloc(sizeof(Duck));
if (!duck) {
return duck;
}
duck->name = name;
return duck;
}
char * duck_name(Duck *self)
{
assert(self);
return self->name;
}
char * duck_speak(Duck *self)
{
assert(self);
return "Pack pack";
}
void duck_free(void *self)
{
if (!self) {
return;
}
free(self);
}
#ifndef DUCK_H
#define DUCK_H
typedef struct duck Duck;
Duck * duck_new(char name[]);
char * duck_name(Duck *self);
char * duck_speak(Duck *self);
void duck_free(void *self);
#endif // DUCK_H
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include "animal.h"
#include "dog.h"
int main(void)
{
// Create an array of quasi-polymorphic objects.
Animal *animals[] ={
animal_new(ANIMAL_TYPE_DUCK, "Michael"),
animal_new(ANIMAL_TYPE_DOG, "Tommy"),
animal_new(ANIMAL_TYPE_TIGER, "Alice")
};
// Quasi-polymorphic calls.
for (size_t i = 0; i < 3; i++) {
printf("%s %s\n", animal_name(animals[i]), animal_speak(animals[i]));
}
// Extract Dog object from Animal object.
Dog *dog = (Dog *) animal_raw(animals[1]);
printf("Dog %s\n", dog_speak(dog));
// Quasi-polymorphically free memory.
for (size_t i = 0; i < 3; i++) {
animal_free(animals[i]);
}
return 0;
}
#include <assert.h>
#include <stdlib.h>
#include "tiger.h"
struct tiger {
char *name;
};
Tiger * tiger_new(char *name)
{
Tiger *tiger = malloc(sizeof(Tiger));
if (!tiger) {
return tiger;
}
tiger->name = name;
return tiger;
}
char * tiger_name(Tiger *self)
{
assert(self);
return self->name;
}
char * tiger_speak(Tiger *self)
{
assert(self);
return "Halum halum";
}
void tiger_free(void *self)
{
if (!self) {
return;
}
free(self);
}
#ifndef TIGER_H
#define TIGER_H
typedef struct tiger Tiger;
Tiger * tiger_new(char *name);
char * tiger_name(Tiger *self);
char * tiger_speak(Tiger *self);
void tiger_free(void *self);
#endif // TIGER_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment