Last active
May 2, 2023 02:22
-
-
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)
This file contains hidden or 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
// 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