194 lines
5.3 KiB
C
194 lines
5.3 KiB
C
// object: method dispatch tables
|
|
|
|
#define ctor(obj) obj_method0(obj, ctor) // ctor[obj_typeid(obj)](obj)
|
|
#define dtor(obj) obj_method0(obj, dtor) // dtor[obj_typeid(obj)](obj)
|
|
|
|
API extern void (*ctor[256])(); ///-
|
|
API extern void (*dtor[256])(); ///-
|
|
|
|
const char *obj_typeof( const void *obj ) {
|
|
int obj_typeeq(a,b)
|
|
|
|
// ---
|
|
|
|
|
|
// ---
|
|
|
|
void *obj_copy(void **dst, const void *src) {
|
|
if(!*dst) return *dst = obj_clone(src);
|
|
|
|
if( obj_typeeq(*dst, src) ) {
|
|
return memcpy(*dst, src, obj_sizeof(src));
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void *obj_mutate(void **dst_, const void *src) {
|
|
// mutate a class. ie, convert a given object class into a different one,
|
|
// while preserving the original metas and references as much as possible.
|
|
//
|
|
// @fixme: systems might be tracking objects in the future. the fact that we
|
|
// can reallocate a pointer (and hence, change its address) seems way too dangerous,
|
|
// as the tracking systems could crash when referencing a mutated object.
|
|
// solutions: do not reallocate if sizeof(new_class) > sizeof(old_class) maybe? good enough?
|
|
// also, optimization hint: no need to reallocate if both sizes matches, just copy contents.
|
|
|
|
if(!*dst_) return *dst_ = obj_clone(src);
|
|
|
|
void *dst = *dst_;
|
|
dtor(dst);
|
|
|
|
unsigned src_sz = obj_sizeof(src);
|
|
unsigned src_id = obj_typeid(src);
|
|
|
|
void *dst_ptr = *((void**)dst - 1);
|
|
unsigned payload = (OBJPAYLOAD16(dst_ptr) & 255) | src_id << 8;
|
|
FREE( OBJUNBOX(dst_ptr) );
|
|
*((void**)dst - 1) = OBJBOX( STRDUP( OBJUNBOX(*((void**)src - 1)) ), payload);
|
|
|
|
void *base = (void*)((void**)dst - 1);
|
|
base = REALLOC(base, src_sz + sizeof(void*));
|
|
*dst_ = (char*)base + sizeof(void*);
|
|
dst = (char*)base + sizeof(void*);
|
|
memcpy(dst, src, src_sz);
|
|
|
|
ctor(dst);
|
|
return dst;
|
|
}
|
|
|
|
|
|
#ifdef OBJ_DEMO
|
|
|
|
typedef struct MyObject {
|
|
char* id;
|
|
int x,y;
|
|
float rotation;
|
|
struct MyObject *next;
|
|
} MyObject;
|
|
|
|
void tests1() {
|
|
// Construct two objects
|
|
MyObject *root = obj_new(MyObject, 0);
|
|
MyObject *obj = obj_new(MyObject, "An identifier!", 0x11, 0x22, 3.1415f, root );
|
|
|
|
// Dump contents of our objects
|
|
|
|
obj_hexdump(root);
|
|
obj_hexdump(obj);
|
|
|
|
// Save to mem
|
|
|
|
char* buffer = obj_save(obj);
|
|
printf("%d bytes\n", (int)strlen(buffer));
|
|
|
|
// Clear
|
|
|
|
obj_zero( obj );
|
|
obj_hexdump( obj );
|
|
|
|
// Reload
|
|
|
|
obj_load( obj, buffer );
|
|
obj_hexdump( obj );
|
|
|
|
// Copy tests
|
|
|
|
{
|
|
MyObject *clone = obj_clone(obj);
|
|
obj_hexdump(clone);
|
|
obj_del(clone);
|
|
}
|
|
|
|
{
|
|
MyObject *copy = 0;
|
|
obj_copy(©, obj);
|
|
obj_hexdump(copy);
|
|
obj_del(copy);
|
|
}
|
|
|
|
{
|
|
MyObject *copy = obj_new(MyObject, "A different identifier!", 0x33, 0x44, 0.0f, root );
|
|
obj_copy(©, obj);
|
|
obj_hexdump(copy);
|
|
obj_del(copy);
|
|
}
|
|
|
|
{
|
|
void *copy = obj_malloc(100, "an untyped class" );
|
|
obj_mutate(©, obj);
|
|
obj_hexdump(copy);
|
|
obj_copy(©, obj);
|
|
obj_hexdump(copy);
|
|
obj_del(copy);
|
|
}
|
|
|
|
// Benchmarking call overhead.
|
|
// We're here using dtor as a method to test. Since there is actually no
|
|
// destructor associated to this class, it will be safe to call it extensively (no double frees).
|
|
//
|
|
// results:
|
|
// 427 million calls/s @ old i5-4300/1.90Ghz laptop. compiled with "cl /Ox /Os /MT /DNDEBUG /GL /GF /arch:AVX2"
|
|
|
|
#ifndef N
|
|
#define N (INT32_MAX-1)
|
|
#endif
|
|
|
|
double t = (puts("benchmarking..."), -clock() / (double)CLOCKS_PER_SEC);
|
|
for( int i = 0; i < N; ++i ) {
|
|
dtor(root);
|
|
}
|
|
t += clock() / (double)CLOCKS_PER_SEC;
|
|
printf("Benchmark: %5.2f objcalls/s %5.2fM objcalls/s\n", N/(t), (N/1000)/(t*1000)); // ((N+N)*5) / (t) );
|
|
|
|
}
|
|
|
|
void tests2() {
|
|
REGISTER_BOX
|
|
REGISTER_RECT
|
|
|
|
box *b = obj_new(box, 100);
|
|
rect *r = obj_new(rect, 100, 200);
|
|
|
|
dump(b);
|
|
dump(r);
|
|
|
|
printf("%f\n", area(b));
|
|
printf("%f\n", area(r));
|
|
|
|
obj_del(b);
|
|
obj_ref(r); obj_unref(r); //obj_del(r);
|
|
|
|
int *untyped = obj_malloc( sizeof(int) );
|
|
int *my_number = obj_malloc( sizeof(int), "a comment about my_number" );
|
|
char *my_text = obj_malloc( 32, "some debug info here" );
|
|
|
|
*untyped = 100;
|
|
*my_number = 123;
|
|
sprintf( my_text, "hello world" );
|
|
|
|
struct my_bitmap { int w, h, bpp; const char *pixels; };
|
|
struct my_bitmap *my_bitmap = obj_new(struct my_bitmap, 2,2,8, "\1\2\3\4");
|
|
|
|
printf( "%p(%s,%u)\n", my_bitmap, obj_typeof(my_bitmap), obj_typeid(my_bitmap) );
|
|
printf( "%d(%s,%d)\n", *untyped, obj_typeof(untyped), obj_typeid(untyped) );
|
|
printf( "%d(%s,%d)\n", *my_number, obj_typeof(my_number), obj_typeid(my_number) );
|
|
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
|
|
|
|
obj_printf(my_text, "hello world #1\n");
|
|
obj_printf(my_text, "hello world #2\n");
|
|
puts(obj_output(my_text));
|
|
|
|
printf( "%s(%s,%d)\n", my_text, obj_typeof(my_text), obj_typeid(my_text) );
|
|
|
|
printf( "equal?:%d\n", obj_typeeq(my_number, untyped) );
|
|
printf( "equal?:%d\n", obj_typeeq(my_number, my_number) );
|
|
printf( "equal?:%d\n", obj_typeeq(my_number, my_text) );
|
|
printf( "equal?:%d\n", obj_typeeq(my_number, my_bitmap) );
|
|
|
|
obj_free( untyped );
|
|
obj_free( my_text );
|
|
obj_free( my_bitmap );
|
|
obj_del( my_number ); // should not crash, even if allocated with obj_malloc()
|
|
}
|