Skip to content

Instantly share code, notes, and snippets.

@nilium
Created January 3, 2010 06:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nilium/bcf9314d2395c2361008 to your computer and use it in GitHub Desktop.
Save nilium/bcf9314d2395c2361008 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
typedef struct s_object object_t;
typedef struct s_class class_t;
typedef void (*cd_method_t)(object_t*);
typedef void (*method_t)(object_t*, ...);
/** junk that I put in to satisfy myself **/
typedef struct s_method_info {
char *name;
char *signature;
method_t method;
} method_info_t;
typedef struct s_ivar_info {
ptrdiff_t offset;
char *name;
char *type;
} ivar_info_t;
/** this sort of counts **/
struct s_class {
class_t *super;
char *name;
size_t instance_size;
cd_method_t initializer;
cd_method_t finalizer;
size_t num_methods;
method_info_t method_info[];
size_t num_ivars;
ivar_info_t ivars_info[];
};
struct s_object {
class_t *clas;
uint32_t refs;
size_t size; // size of the ivars block
void *ivars; // <- THIS part counts: actual ivar data stored separate from the object
};
// not that important
void class_instantiate__init(object_t *obj, class_t *clas, class_t *until) {
if (clas->super != until)
class_instantiate__init(obj, clas->super, until);
if (clas->initializer != NULL)
clas->initializer(obj);
}
// sort of important?
object_t *class_instantiate(class_t *clas) {
object_t *obj = (object_t*)malloc(sizeof(object_t));
obj->clas = clas;
obj->refs = 1;
obj->ivars = (clas->instance_size == 0 ? NULL : (void*)malloc(clas->instance_size));
class_instantiate__init(obj, clas, NULL);
return obj;
}
method_info_t *class_findMethod(class_t *clas, const char *name) {
while (clas != NULL) {
size_t index = 0;
for (; index < clas->num_methods; ++index) {
if (strcmp(clas->method_info[index].name, name) == 0)
return clas->method_info+index;
}
clas = clas->super;
}
return NULL;
}
ivar_info_t *class_findIvar(class_t *clas, const char *name) {
while (clas != NULL) {
size_t index = 0;
for (; index < clas->num_ivars; ++index) {
if (strcmp(clas->ivars_info[index].name, name) == 0)
return clas->ivars_info+index;
}
clas = clas->super;
}
return NULL;
}
// what I was getting at
// returns old class (newly introduced ivars are set to their default values)
class_t *object_setClass(object_t *obj, class_t *clas) {
if (obj->clas == clas)
return clas;
class_t *oldClass = obj->clas;
void *ivars = obj->ivars;
if (clas->instance_size == 0 && ivars != NULL) {
free(ivars);
ivars = NULL;
} else if (obj->size < clas->instance_size) {
ivars = (void*)realloc(ivars, clas->instance_size);
// realloc error
if (ivars == NULL) {
fprintf(stderr, "Error reallocating ivars for %s@%x while setting class to %s\n",
oldClass->name, (unsigned int)obj, clas->name);
exit(1);
}
obj->size = clas->instance_size;
}
obj->clas = clas;
obj->ivars = ivars;
for(; clas != NULL; clas = clas->super) {
class_t *superOld = oldClass;
for(; superOld != NULL && superOld != clas; superOld = superOld->super);
if (superOld != NULL)
break;
}
class_instantiate__init(obj, obj->clas, clas);
return oldClass;
}
object_t *object_retain(object_t *obj) {
if (obj != NULL)
++obj->refs;
return obj;
}
void object_destroy(object_t *obj) {
class_t *clas = obj->clas;
for (; clas != NULL; clas = clas->super)
if (clas->finalizer != NULL) clas->finalizer(obj);
if (obj->ivars != NULL)
free(obj->ivars);
free(obj);
}
void object_release(object_t *obj) {
if (obj == NULL)
return;
if (--obj->refs == 0) {
object_destroy(obj);
}
}
void object_getIvar(object_t *obj, const char *name, void **value) {
ivar_info_t *ivar = class_findIvar(obj->clas, name);
if (ivar == NULL) {
return;
}
switch (ivar->type[0]) {
case '@': {// object
*(object_t**)value = object_retain(*(object_t**)(obj->ivars+ivar->offset));
} break;
case '*': // pointer
*value = *(void**)(obj->ivars+ivar->offset);
break;
case '#': // function pointer
*(method_t**)value = *(method_t**)(obj->ivars+ivar->offset);
break;
case '&': // value
switch(ivar->type[2]) {
case 'b': *(uint8_t*)value = *(uint8_t*)(obj->ivars+ivar->offset); break;
case 's': *(uint16_t*)value = *(uint16_t*)(obj->ivars+ivar->offset); break;
case 'i': *(uint32_t*)value = *(uint32_t*)(obj->ivars+ivar->offset); break;
case 'l': *(uint64_t*)value = *(uint64_t*)(obj->ivars+ivar->offset); break;
case 'f': *(float*)value = *(float*)(obj->ivars+ivar->offset); break;
case 'd': *(double*)value = *(double*)(obj->ivars+ivar->offset); break;
}
break;
default: exit(1); // TODO: I DON'T KNOW WHAT TO DO ANYMORE
}
}
void object_setIvar(object_t *obj, const char *name, ...) {
va_list args;
ivar_info_t *ivar = class_findIvar(obj->clas, name);
if (ivar == NULL) {
return;
}
va_start(args, name);
switch (ivar->type[0]) {
case '@': {// object
object_t *newvalue, *oldvalue, **pointer;
pointer = (object_t**)(obj->ivars+ivar->offset);
newvalue = va_arg(args, object_t*);
oldvalue = *pointer;
*pointer = object_retain(newvalue);
object_release(oldvalue);
} break;
case '*': // pointer
*(void**)(obj->ivars+ivar->offset) = va_arg(args, void*);
break;
case '#': // function pointer
*(method_t*)(obj->ivars+ivar->offset) = va_arg(args, method_t);
break;
case '&': // value
switch(ivar->type[2]) {
case 'b': *(uint8_t*)(obj->ivars+ivar->offset) = va_arg(args, int); break;
case 's': *(uint16_t*)(obj->ivars+ivar->offset) = va_arg(args, int); break;
case 'i': *(uint32_t*)(obj->ivars+ivar->offset) = va_arg(args, uint32_t); break;
case 'l': *(uint64_t*)(obj->ivars+ivar->offset) = va_arg(args, uint64_t); break;
case 'f': *(float*)(obj->ivars+ivar->offset) = (float)va_arg(args, double); break;
case 'd': *(double*)(obj->ivars+ivar->offset) = va_arg(args, double); break;
}
break;
default: exit(1);
}
va_end(args);
}
#define object_invokeMethod(object, name, args...)\
class_findMethod((object)->clas, (name))->method((object), ## args)
// note: all of this compiles, but like any intelligent person would do, i did not see if it actually works (probably doesn't)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment