Last active
May 30, 2022 23:35
-
-
Save cwchentw/42780178b3948c1dd94454e934fc05f3 to your computer and use it in GitHub Desktop.
Polymorphism with Union in C (Apache 2.0)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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