311 lines
12 KiB
C
311 lines
12 KiB
C
// C objects framework
|
|
// - rlyeh, public domain.
|
|
//
|
|
// ## object limitations
|
|
// - 8-byte overhead per object
|
|
// - XX-byte overhead per object-entity
|
|
// - 32 components max per object-entity
|
|
// - 256 classes max per game
|
|
// - 256 references max per object
|
|
// - 1024K bytes max per object
|
|
// - 8 generations + 64K IDs per running instance (19-bit IDs)
|
|
// - support for pragma pack(1) structs not enabled by default.
|
|
|
|
/* /!\ if you plan to use pragma pack(1) on any struct, you need #define OBJ_MIN_PRAGMAPACK_BITS 0 at the expense of max class size /!\ */
|
|
#ifndef OBJ_MIN_PRAGMAPACK_BITS
|
|
//#define OBJ_MIN_PRAGMAPACK_BITS 3 // allows pragma packs >= 8. objsizew becomes 8<<3, so 2048 bytes max per class (default)
|
|
#define OBJ_MIN_PRAGMAPACK_BITS 2 // allows pragma packs >= 4. objsizew becomes 8<<2, so 1024 bytes max per class
|
|
//#define OBJ_MIN_PRAGMAPACK_BITS 1 // allows pragma packs >= 2. objsizew becomes 8<<1, so 512 bytes max per class
|
|
//#define OBJ_MIN_PRAGMAPACK_BITS 0 // allows pragma packs >= 1. objsizew becomes 8<<0, so 256 bytes max per class
|
|
#endif
|
|
|
|
#define OBJHEADER \
|
|
struct { \
|
|
ifdef(debug, const char *objname;) \
|
|
union { \
|
|
uintptr_t objheader; \
|
|
struct { \
|
|
uintptr_t objtype:8; \
|
|
uintptr_t objsizew:8; \
|
|
uintptr_t objrefs:8; \
|
|
uintptr_t objheap:1; \
|
|
uintptr_t objcomps:1; /* << can be removed? check payload ptr instead? */ \
|
|
uintptr_t objunused:64-8-8-8-1-1-ID_INDEX_BITS-ID_COUNT_BITS; /*19*/ \
|
|
uintptr_t objid:ID_INDEX_BITS+ID_COUNT_BITS; /*16+3*/ \
|
|
}; \
|
|
}; \
|
|
array(struct obj*) objchildren; \
|
|
};
|
|
|
|
#define OBJ \
|
|
OBJHEADER
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// syntax sugars
|
|
|
|
#ifdef OBJTYPE
|
|
#undef OBJTYPE
|
|
#endif
|
|
|
|
#define OBJTYPE(T) \
|
|
OBJTYPE_##T
|
|
|
|
#define OBJTYPEDEF(NAME,N) \
|
|
enum { OBJTYPE(NAME) = N }; \
|
|
STATIC_ASSERT( N <= 255 ); \
|
|
STATIC_ASSERT( sizeof(NAME) == ((sizeof(NAME)>>OBJ_MIN_PRAGMAPACK_BITS)<<OBJ_MIN_PRAGMAPACK_BITS) ); // (sizeof(NAME) & ((1<<OBJ_MIN_PRAGMAPACK_BITS)-1)) == 0 );
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// objects
|
|
|
|
#define TYPEDEF_STRUCT(NAME,N,...) \
|
|
typedef struct NAME { OBJ \
|
|
__VA_ARGS__ \
|
|
char payload[0]; \
|
|
} NAME; OBJTYPEDEF(NAME,N);
|
|
|
|
// TYPEDEF_STRUCT(obj,0);
|
|
typedef struct obj { OBJ } obj;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// entities
|
|
|
|
#define OBJCOMPONENTS_MAX 32
|
|
#define OBJCOMPONENTS_ALL_ENABLED 0xAAAAAAAAAAAAAAAAULL
|
|
#define OBJCOMPONENTS_ALL_FLAGGED 0x5555555555555555ULL
|
|
#define COMPONENTS_ONLY(x) ((x) & ~OBJCOMPONENTS_ALL_FLAGGED)
|
|
|
|
#define ENTITY \
|
|
struct { OBJHEADER union { struct { uintptr_t objenabled:OBJCOMPONENTS_MAX, objflagged:OBJCOMPONENTS_MAX; }; uintptr_t cflags; }; void *c[OBJCOMPONENTS_MAX]; };
|
|
|
|
#define TYPEDEF_ENTITY(NAME,N,...) \
|
|
typedef struct NAME { ENTITY \
|
|
__VA_ARGS__ \
|
|
char payload[0]; \
|
|
} NAME; OBJTYPEDEF(NAME,N);
|
|
|
|
// OBJTYPEDEF(entity,1)
|
|
typedef struct entity { ENTITY } entity;
|
|
|
|
#define entity_new(TYPE, ...) OBJ_CTOR(TYPE, #TYPE, 1, 0, __VA_ARGS__)
|
|
#define entity_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, 0, __VA_ARGS__)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// heap/stack ctor/dtor
|
|
|
|
static __thread obj *objtmp;
|
|
#define OBJ_CTOR_HDR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE) ( \
|
|
(PTR)->objheader = HEAP ? id_make(PTR) : 0, /*should assign to .objid instead. however, id_make() returns shifted bits already*/ \
|
|
(PTR)->objtype = (OBJ_TYPE), \
|
|
(PTR)->objheap = (HEAP), \
|
|
(PTR)->objsizew = (SIZEOF_OBJ>>OBJ_MIN_PRAGMAPACK_BITS))
|
|
#define OBJ_CTOR_PTR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE) ( \
|
|
OBJ_CTOR_HDR(PTR,HEAP,SIZEOF_OBJ,OBJ_TYPE), \
|
|
obj_ctor(PTR))
|
|
#define OBJ_CTOR(TYPE, NAME, HEAP, PAYLOAD_SIZE, ...) (TYPE*)( \
|
|
objtmp = (HEAP ? MALLOC(sizeof(TYPE)+(PAYLOAD_SIZE)) : ALLOCA(sizeof(TYPE)+(PAYLOAD_SIZE))), \
|
|
*(TYPE*)objtmp = ((TYPE){ {0,}, __VA_ARGS__}), \
|
|
((PAYLOAD_SIZE) ? memset((char*)objtmp + sizeof(TYPE), 0, (PAYLOAD_SIZE)) : objtmp), \
|
|
( OBJTYPES[ OBJTYPE(TYPE) ] = #TYPE ), \
|
|
OBJ_CTOR_PTR(objtmp, HEAP,sizeof(TYPE),OBJTYPE(TYPE)), \
|
|
ifdef(debug, (obj_printf)(objtmp, va("%s", callstack(+16))), 0), \
|
|
obj_setname(objtmp, NAME))
|
|
|
|
#define obj(TYPE, ...) *OBJ_CTOR(TYPE, #TYPE, 0, 0, __VA_ARGS__)
|
|
#define obj_new(TYPE, ...) OBJ_CTOR(TYPE, #TYPE, 1, 0, __VA_ARGS__)
|
|
#define obj_new_ext(TYPE, NAME, ...) OBJ_CTOR(TYPE, NAME, 1, 0, __VA_ARGS__)
|
|
|
|
void* obj_malloc(unsigned sz);
|
|
void* obj_free(void *o);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// obj generics. can be extended.
|
|
|
|
#define obj_ctor(o,...) obj_method(ctor, o, ##__VA_ARGS__)
|
|
#define obj_dtor(o,...) obj_method(dtor, o, ##__VA_ARGS__)
|
|
|
|
#define obj_save(o,...) obj_method(save, o, ##__VA_ARGS__)
|
|
#define obj_load(o,...) obj_method(load, o, ##__VA_ARGS__)
|
|
|
|
#define obj_test(o,...) obj_method(test, o, ##__VA_ARGS__)
|
|
|
|
#define obj_init(o,...) obj_method(init, o, ##__VA_ARGS__)
|
|
#define obj_quit(o,...) obj_method(quit, o, ##__VA_ARGS__)
|
|
#define obj_tick(o,...) obj_method(tick, o, ##__VA_ARGS__)
|
|
#define obj_draw(o,...) obj_method(draw, o, ##__VA_ARGS__)
|
|
|
|
#define obj_lerp(o,...) obj_method(lerp, o, ##__VA_ARGS__)
|
|
#define obj_edit(o,...) obj_method(edit, o, ##__VA_ARGS__)
|
|
|
|
// --- syntax sugars
|
|
|
|
#define obj_extend(T,func) (obj_##func[OBJTYPE(T)] = (void*)T##_##func)
|
|
#define obj_method(method,o,...) (obj_##method[((obj*)(o))->objtype](o,##__VA_ARGS__)) // (obj_##method[((obj*)(o))->objtype]((o), ##__VA_ARGS__))
|
|
|
|
#define obj_vtable(func,RC,...) RC macro(obj_##func)(){ __VA_ARGS__ }; RC (*obj_##func[256])() = { REPEAT256(macro(obj_##func)) };
|
|
#define obj_vtable_null(func,RC) RC (*obj_##func[256])() = { 0 }; // null virtual table. will crash unless obj_extend'ed
|
|
|
|
#define REPEAT16(f) f,f,f,f,f,f,f,f,f,f,f,f,f,f,f,f
|
|
#define REPEAT64(f) REPEAT16(f),REPEAT16(f),REPEAT16(f),REPEAT16(f)
|
|
#define REPEAT256(f) REPEAT64(f),REPEAT64(f),REPEAT64(f),REPEAT64(f)
|
|
|
|
// --- declare vtables
|
|
|
|
API extern void (*obj_ctor[256])(); ///-
|
|
API extern void (*obj_dtor[256])(); ///-
|
|
|
|
API extern char* (*obj_save[256])(); ///-
|
|
API extern bool (*obj_load[256])(); ///-
|
|
API extern int (*obj_test[256])(); ///-
|
|
|
|
API extern int (*obj_init[256])(); ///-
|
|
API extern int (*obj_quit[256])(); ///-
|
|
API extern int (*obj_tick[256])(); ///-
|
|
API extern int (*obj_draw[256])(); ///-
|
|
|
|
API extern int (*obj_lerp[256])(); ///-
|
|
API extern int (*obj_edit[256])(); ///-
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// core
|
|
|
|
API uintptr_t obj_header(const void *o);
|
|
API uintptr_t obj_id(const void *o);
|
|
|
|
API const char* obj_type(const void *o);
|
|
API unsigned obj_typeid(const void *o);
|
|
|
|
API int obj_sizeof(const void *o);
|
|
API int obj_size(const void *o); // size of all members together in struct. may include padding bytes.
|
|
|
|
API char* obj_data(void *o); // pointer to the first member in struct
|
|
API const char* obj_datac(const void *o); // const pointer to the first struct member
|
|
|
|
API void* obj_payload(const void *o); // pointer right after last member in struct
|
|
API void* obj_zero(void *o); // reset all object members
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// refcounting
|
|
|
|
API void* obj_ref(void *oo);
|
|
API void* obj_unref(void *oo);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// scene tree
|
|
|
|
#define each_objchild(p,T,o) /*non-recursive*/ \
|
|
(array(obj*)* children = obj_children(p); children; children = 0) \
|
|
for(int _i = 1, _end = array_count(*children); _i < _end; ++_i) \
|
|
for(T o = (T)((*children)[_i]); o && (obj_parent(o) == p); o = 0)
|
|
|
|
API obj* obj_detach(void *c);
|
|
API obj* obj_attach(void *o, void *c);
|
|
|
|
API obj* obj_root(const void *o);
|
|
API obj* obj_parent(const void *o);
|
|
API array(obj*)*obj_children(const void *o); // child[0]: parent, child[1]: 1st child, child[2]: 2nd child...
|
|
API array(obj*)*obj_siblings(const void *o); // child[0]: grandpa, child[1]: sibling1, child[2]: sibling2...
|
|
|
|
API int obj_dumptree(const void *o);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// metadata
|
|
|
|
API void* obj_setmeta(void *o, const char *key, const char *value);
|
|
API const char* obj_meta(const void *o, const char *key);
|
|
|
|
API void* obj_setname(void *o, const char *name);
|
|
API const char* obj_name(const void *o);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// stl
|
|
|
|
API void* obj_swap(void *dst, void *src);
|
|
API void* obj_copy_fast(void *dst, const void *src);
|
|
API void* obj_copy(void *dst, const void *src);
|
|
|
|
API int obj_comp_fast(const void *a, const void *b);
|
|
API int obj_comp(const void *a, const void *b);
|
|
API int obj_lesser(const void *a, const void *b);
|
|
API int obj_greater(const void *a, const void *b);
|
|
API int obj_equal(const void *a, const void *b);
|
|
|
|
API uint64_t obj_hash(const void *o);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// debug
|
|
|
|
API bool obj_hexdump(const void *oo);
|
|
API int obj_print(const void *o);
|
|
|
|
API int obj_printf(const void *o, const char *text);
|
|
API int obj_console(const void *o); // obj_output() ?
|
|
|
|
#define obj_printf(o, ...) obj_printf(o, va(__VA_ARGS__))
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// serialization
|
|
|
|
API char* obj_saveini(const void *o);
|
|
API obj* obj_mergeini(void *o, const char *ini);
|
|
API obj* obj_loadini(void *o, const char *ini);
|
|
|
|
API char* obj_savejson(const void *o);
|
|
API obj* obj_mergejson(void *o, const char *json);
|
|
API obj* obj_loadjson(void *o, const char *json);
|
|
|
|
API char* obj_savebin(const void *o);
|
|
API obj* obj_mergebin(void *o, const char *sav);
|
|
API obj* obj_loadbin(void *o, const char *sav);
|
|
|
|
API char* obj_savempack(const void *o); // @todo
|
|
API obj* obj_mergempack(void *o, const char *sav); // @todo
|
|
API obj* obj_loadmpack(void *o, const char *sav); // @todo
|
|
|
|
API int obj_push(const void *o);
|
|
API int obj_pop(void *o);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// components
|
|
|
|
API bool obj_addcomponent(entity *e, unsigned c, void *ptr);
|
|
API bool obj_hascomponent(entity *e, unsigned c);
|
|
API void* obj_getcomponent(entity *e, unsigned c);
|
|
API bool obj_delcomponent(entity *e, unsigned c);
|
|
API bool obj_usecomponent(entity *e, unsigned c);
|
|
API bool obj_offcomponent(entity *e, unsigned c);
|
|
|
|
API char* entity_save(entity *self);
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// reflection
|
|
|
|
#define each_objmember(oo,TYPE,NAME,PTR) \
|
|
(array(reflect_t) *found_ = members_find(obj_type(oo)); found_; found_ = 0) \
|
|
for(int it_ = 0, end_ = array_count(*found_); it_ != end_; ++it_ ) \
|
|
for(reflect_t *R = &(*found_)[it_]; R; R = 0 ) \
|
|
for(const char *NAME = R->name, *TYPE = R->type; NAME || TYPE; ) \
|
|
for(void *PTR = ((char*)oo) + R->sz ; NAME || TYPE ; NAME = TYPE = 0 )
|
|
|
|
API void* obj_clone(const void *src);
|
|
API void* obj_merge(void *dst, const void *src); // @testme
|
|
API void* obj_mutate(void *dst, const void *src);
|
|
API void* obj_make(const char *str);
|
|
|
|
// built-ins
|
|
|
|
typedef enum OBJTYPE_BUILTINS {
|
|
OBJTYPE_obj = 0,
|
|
OBJTYPE_entity = 1,
|
|
OBJTYPE_vec2 = 2,
|
|
OBJTYPE_vec3 = 3,
|
|
OBJTYPE_vec4 = 4,
|
|
OBJTYPE_quat = 5,
|
|
OBJTYPE_mat33 = 6,
|
|
OBJTYPE_mat34 = 7,
|
|
OBJTYPE_mat44 = 8,
|
|
OBJTYPE_vec2i = 9,
|
|
OBJTYPE_vec3i = 10,
|
|
} OBJTYPE_BUILTINS;
|
|
|