Skip to content

Instantly share code, notes, and snippets.

@fami-com
Last active May 2, 2023 02:22
Show Gist options
  • Save fami-com/c2e06b0f0346df3be8adbac259b1e05e to your computer and use it in GitHub Desktop.
Save fami-com/c2e06b0f0346df3be8adbac259b1e05e to your computer and use it in GitHub Desktop.
C OOP proof of concept (very bad, don't actually use in real life)
// All of this is licensed under the CC0 licence btw
#include <string.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#define S_(class, func, ...) _##class##_##func(__VA_ARGS__)
#define S(class, func, ...) S_(class, func __VA_OPT__(,) __VA_ARGS__)
#define new(class, ...) S(class, new, __VA_ARGS__)
#define init(class, ...) S(class, init, __VA_ARGS__);
// GCC extensions, fuck you MSVC users
// This will segfault if you're calling a function not in the vtable
#define C_(obj, func, ...) ({ __auto_type o = obj; \
VtableFn *fun = vtable_lookup(o->_v, #func); \
fun(o __VA_OPT__(,) __VA_ARGS__); })
#define C(obj, func, ...) C_(obj, func __VA_OPT__(,) __VA_ARGS__)
#define delete(obj) C(obj, delete)
#define VE(class, func) { #func, (VtableFn*)_##class##_##func }
#define TABS "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
#define ARRSZ(arr) (sizeof(arr)/(sizeof(*(arr))))
typedef void *VtableFn();
//typedef void *VtableFn(...);
struct vtable_item {
char name[32];
VtableFn *_f;
};
struct vtable {
void *prev;
size_t nitems;
// yes yes, this is just a linear buffer of functions, I'm too lazy to code a hash table for a
// stupid proof of concept
struct vtable_item data[];
};
// Since every function has to return Object or be void, everything has to be boxed.
// Why are you waving at Python?
typedef struct Object {
struct vtable *_v;
void *_d;
} *var, *func, *Object;
struct vtable *vtable_new(void *prev, size_t nitems, struct vtable_item *data) {
struct vtable *vt = malloc(sizeof(struct vtable) + nitems*sizeof(struct vtable_item));
vt->prev = prev;
vt->nitems = nitems;
memcpy(vt->data, data, nitems*sizeof(*data));
return vt;
}
VtableFn *vtable_lookup(struct vtable *vt, char *name) {
//printf("Trying to call %s\n", name);
VtableFn *res = NULL;
// O(N*M) search, where N is the number of entries in each table and M is the depth of inheritance
do {
for (size_t i = 0; i < vt->nitems; i++) {
if (strcmp(name, vt->data[i].name) == 0) {
res = vt->data[i]._f;
goto exit;
}
}
vt = vt->prev;
} while(!res && vt);
exit:
return res;
}
// probably the only sane part of this stupid project
// prints pretty nested vtables in a pretty pretty manner
void _print_vtable(struct vtable *vt, int depth) {
if (!vt) {
printf("Vtable(null)");
return;
}
printf("Vtable(%p) {\n", vt);
printf("%.*s.prev = ", depth, TABS);
_print_vtable(vt->prev, depth+1);
printf(",\n%.*s.nitems = %zu,\n", depth, TABS, vt->nitems);
printf("%.*s.items = {\n", depth, TABS);
for (size_t i = 0; i < vt->nitems; i++) {
printf("%.*s{ .name = %8s, .func = %p },\n", depth+2, TABS, vt->data[i].name, vt->data[i]._f);
}
printf("%.*s}\n", depth+1, TABS);
printf("%.*s}", depth-1, TABS);
}
void print_vtable(struct vtable *vt) {
_print_vtable(vt, 1);
puts("");
}
typedef struct Vec2D {
double x, y;
} *Vec2D;
typedef struct Vec3D {
struct Vec2D d; // subclassing is just inclusion as the first element. Sorry, no multiple inheritance
double z;
} *Vec3D;
typedef struct Double {
double d;
} *Double;
func _Vec2D_new(double x, double y);
func _Vec3D_new(double x, double y, double z);
func _Double_new(double d);
func _Object_new(void);
// Start Object definitions
// default dtor
void _Object_delete(struct Object *obj) {
free(obj->_d);
free(obj);
}
void _Object_show(struct Object *obj) {
printf("Object(%p)\n", obj);
}
struct vtable *_object_vtable = NULL;
struct vtable_item _object_vtable_items[] = {
VE(Object, show),
VE(Object, delete),
};
void object_init_vtable(void) {
_object_vtable = vtable_new(NULL, ARRSZ(_object_vtable_items), _object_vtable_items);
}
func _Object_new() {
struct Object *obj = malloc(sizeof(*obj));
if (!_object_vtable) object_init_vtable();
obj->_v = _object_vtable;
obj->_d = NULL;
return obj;
}
// Start Vec2D definitions
func _Vec2D_add(struct Object *lhs, struct Object *rhs) {
Vec2D _lhs = lhs->_d;
Vec2D _rhs = rhs->_d;
double x = _lhs->x + _rhs->x;
double y = _lhs->y + _rhs->y;
return new(Vec2D, x, y);
}
func _Vec2D_sub(struct Object *lhs, struct Object *rhs) {
Vec2D _lhs = lhs->_d;
Vec2D _rhs = rhs->_d;
double x = _lhs->x - _rhs->x;
double y = _lhs->y - _rhs->y;
return new(Vec2D, x, y);
}
func _Vec2D_dot(struct Object *lhs, struct Object *rhs) {
Vec2D _lhs = lhs->_d;
Vec2D _rhs = rhs->_d;
double x = _lhs->x * _rhs->x;
double y = _lhs->y * _rhs->y;
return new(Double, x + y);
}
void _Vec2D_show(struct Object *obj) {
struct Vec2D *vec = obj->_d;
printf("Vec2D { x. = %f, .y = %f }\n", vec->x, vec->y);
}
struct vtable *_vec2D_vtable = NULL;
struct vtable_item _vec2D_vtable_items[] = {
VE(Vec2D, add),
VE(Vec2D, sub),
VE(Vec2D, dot),
VE(Vec2D, show)
};
void vec2D_init_vtable(void) {
if (!_object_vtable) object_init_vtable();
_vec2D_vtable = vtable_new(_object_vtable, ARRSZ(_vec2D_vtable_items), _vec2D_vtable_items);
}
void _Vec2D_init(struct Vec2D *vec, double x, double y) {
vec->x = x;
vec->y = y;
}
func _Vec2D_new(double x, double y) {
Vec2D vec = malloc(sizeof(*vec));
Object obj = malloc(sizeof(*obj));
if (!_vec2D_vtable) vec2D_init_vtable();
init(Vec2D, vec, x, y);
obj->_v = _vec2D_vtable;
obj->_d = vec;
return obj;
}
// End Vec2D definitions
// Start Vec3D definitions
func _Vec3D_add(struct Object *lhs, struct Object *rhs) {
Vec3D _lhs = lhs->_d;
Vec3D _rhs = rhs->_d;
double x = _lhs->d.x + _rhs->d.x;
double y = _lhs->d.y + _rhs->d.y;
double z = _lhs->z + _rhs->z;
return new(Vec3D, x, y, z);
}
func _Vec3D_sub(struct Object *lhs, struct Object *rhs) {
Vec3D _lhs = lhs->_d;
Vec3D _rhs = rhs->_d;
double x = _lhs->d.x - _rhs->d.x;
double y = _lhs->d.y - _rhs->d.y;
double z = _lhs->z - _rhs->z;
return new(Vec3D, x, y, z);
}
func _Vec3D_dot(struct Object *lhs, struct Object *rhs) {
Vec3D _lhs = lhs->_d;
Vec3D _rhs = rhs->_d;
double x = _lhs->d.x * _rhs->d.x;
double y = _lhs->d.y * _rhs->d.y;
double z = _lhs->z * _rhs->z;
return new(Double, x + y + z);
}
void _Vec3D_show(struct Object *obj) {
Vec3D vec = obj->_d;
printf("Vec3D { x. = %f, .y = %f, .z = %f }\n", vec->d.x, vec->d.y, vec->z);
}
struct vtable *_vec3D_vtable = NULL;
struct vtable_item _vec3D_vtable_items[] = {
VE(Vec3D, add),
VE(Vec3D, sub),
VE(Vec3D, dot),
VE(Vec3D, show),
};
void vec3D_init_vtable(void) {
if (!_vec2D_vtable) vec2D_init_vtable();
_vec3D_vtable = vtable_new(_vec2D_vtable, ARRSZ(_vec3D_vtable_items), _vec3D_vtable_items);
}
void _Vec3D_init(struct Vec3D *vec, double x, double y, double z) {
init(Vec2D, &vec->d, x, y); // equivalent of calling the constructor of the superclass
vec->z = z;
}
func _Vec3D_new(double x, double y, double z) {
Object obj = malloc(sizeof(*obj));
Vec3D vec = malloc(sizeof(*vec));
if (!_vec3D_vtable) vec3D_init_vtable();
init(Vec3D, vec, x, y, z);
obj->_v = _vec3D_vtable;
obj->_d = vec;
return obj;
}
// End Vec3D definitions
// Start Double definitions
void _Double_show(struct Object *obj) {
struct Double *data = obj->_d;
printf("Double { .d = %f }\n", data->d);
}
struct vtable *_double_vtable = NULL;
struct vtable_item _double_vtable_items[] = {
VE(Double, show)
};
void double_init_vtable(void) {
if (!_object_vtable) object_init_vtable();
_double_vtable = vtable_new(_object_vtable, ARRSZ(_double_vtable_items), _double_vtable_items);
}
void _Double_init(Double dbl, double d) {
dbl->d = d;
}
func _Double_new(double d) {
Object obj = malloc(sizeof(*obj));
Double data = malloc(sizeof(*data));
if (!_double_vtable) double_init_vtable();
init(Double, data, d);
obj->_v = _double_vtable;
obj->_d = data;
return obj;
}
// End Double definitions
int main() {
var vec1 = new(Vec3D, 2, 3, 4);
var vec2 = new(Vec3D, 4, 5, 6);
var vec3 = new(Vec2D, 10, 20);
var vec4 = C(vec1, add, vec2);
var vec5 = C(vec3, add, vec1);
var dot = C(vec1, dot, vec2);
C(vec1, show);
C(vec2, show);
C(vec3, show);
C(vec4, show);
C(vec5, show);
C(dot, show);
// look ma, no unfreed memory on exit
delete(vec1);
delete(vec2);
delete(vec3);
delete(vec4);
delete(vec5);
delete(dot);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment