2023-10-11 19:01:06 +00:00
|
|
|
// C reflection: enums, functions, structs, members and anotations.
|
|
|
|
// - rlyeh, public domain
|
|
|
|
//
|
|
|
|
// @todo: nested structs? pointers in members?
|
|
|
|
// @todo: declare TYPEDEF(vec3, float[3]), TYPEDEF(mat4, vec4[4]/*float[16]*/)
|
|
|
|
|
2023-10-15 11:16:35 +00:00
|
|
|
static map(unsigned, reflect_t) reflects;
|
|
|
|
static map(unsigned, array(reflect_t)) members;
|
2023-10-11 19:01:06 +00:00
|
|
|
|
2023-10-16 15:15:59 +00:00
|
|
|
void reflect_init() {
|
|
|
|
if(!reflects) map_init_int(reflects);
|
2023-10-28 12:16:41 +00:00
|
|
|
if(!members) map_init_int(members);
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
2023-10-16 15:15:59 +00:00
|
|
|
AUTORUN {
|
|
|
|
reflect_init();
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
|
|
|
|
2023-11-02 09:54:49 +00:00
|
|
|
const char* symbol_naked(const char *s) {
|
2023-10-20 17:55:43 +00:00
|
|
|
if( strbeg(s, "const ") ) s += 6;
|
|
|
|
if( strbeg(s, "union ") ) s += 6;
|
|
|
|
if( strbeg(s, "struct ") ) s += 7;
|
|
|
|
if(!strstr(s, " *") ) return s;
|
|
|
|
char *copy = va("%s", s);
|
|
|
|
do strswap(copy," *","*"); while( strstr(copy, " *") ); // char * -> char*
|
|
|
|
return (const char *)copy;
|
|
|
|
}
|
|
|
|
|
|
|
|
void type_inscribe(const char *TY,unsigned TYsz,const char *infos) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
unsigned TYid = intern(TY = symbol_naked(TY));
|
2023-11-01 11:24:16 +00:00
|
|
|
map_find_or_add(reflects, TYid, ((reflect_t){TYid, 0, TYsz, STRDUP(TY), infos})); // @leak
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
2023-10-20 17:55:43 +00:00
|
|
|
void enum_inscribe(const char *E,unsigned Eval,const char *infos) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
unsigned Eid = intern(E = symbol_naked(E));
|
2023-11-01 11:24:16 +00:00
|
|
|
map_find_or_add(reflects, Eid, ((reflect_t){Eid,0, Eval, STRDUP(E),infos})); // @leak
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
|
|
|
unsigned enum_find(const char *E) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
E = symbol_naked(E);
|
2023-10-11 19:01:06 +00:00
|
|
|
return map_find(reflects, intern(E))->sz;
|
|
|
|
}
|
2023-10-20 17:55:43 +00:00
|
|
|
void function_inscribe(const char *F,void *func,const char *infos) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
unsigned Fid = intern(F = symbol_naked(F));
|
2023-11-01 11:24:16 +00:00
|
|
|
map_find_or_add(reflects, Fid, ((reflect_t){Fid,0, 0, STRDUP(F),infos, func})); // @leak
|
2023-10-15 11:16:35 +00:00
|
|
|
reflect_t *found = map_find(reflects,Fid);
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
|
|
|
void *function_find(const char *F) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
F = symbol_naked(F);
|
2023-10-11 19:01:06 +00:00
|
|
|
return map_find(reflects, intern(F))->addr;
|
|
|
|
}
|
2023-10-20 17:55:43 +00:00
|
|
|
void struct_inscribe(const char *T,unsigned Tsz,unsigned OBJTYPEid, const char *infos) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
unsigned Tid = intern(T = symbol_naked(T));
|
2023-11-01 11:24:16 +00:00
|
|
|
map_find_or_add(reflects, Tid, ((reflect_t){Tid, OBJTYPEid, Tsz, STRDUP(T), infos})); // @leak
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
2023-11-01 11:24:16 +00:00
|
|
|
void member_inscribe(const char *T, const char *M,unsigned Msz, const char *infos, const char *TYPE, unsigned bytes) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
unsigned Tid = intern(T = symbol_naked(T));
|
|
|
|
unsigned Mid = intern(M = symbol_naked(M));
|
|
|
|
unsigned Xid = intern(TYPE = symbol_naked(TYPE));
|
2023-11-01 11:24:16 +00:00
|
|
|
map_find_or_add(reflects, (Mid<<16)|Tid, ((reflect_t){Mid, 0, Msz, STRDUP(M), infos, NULL, Tid, STRDUP(TYPE) })); // @leak
|
2023-10-11 19:01:06 +00:00
|
|
|
// add member separately as well
|
|
|
|
if(!members) map_init_int(members);
|
2023-10-15 11:16:35 +00:00
|
|
|
array(reflect_t) *found = map_find_or_add(members, Tid, 0);
|
2023-11-01 11:24:16 +00:00
|
|
|
reflect_t data = {Mid, 0, Msz, STRDUP(M), infos, NULL, Tid, STRDUP(TYPE), bytes }; // @leak
|
|
|
|
// ensure member has not been added previously
|
|
|
|
#if 1
|
|
|
|
// works, without altering member order
|
|
|
|
reflect_t *index = 0;
|
|
|
|
for(int i = 0, end = array_count(*found); i < end; ++i) {
|
|
|
|
if( (*found)[i].id == Mid ) { index = (*found)+i; break; }
|
|
|
|
}
|
|
|
|
if( index ) *index = data; else array_push(*found, data);
|
|
|
|
#else
|
|
|
|
// works, although members get sorted
|
|
|
|
array_push(*found, data);
|
|
|
|
array_sort(*found, less_unsigned_ptr); //< first member type in reflect_t is `unsigned id`, so less_unsigned_ptr works
|
|
|
|
array_unique(*found, less_unsigned_ptr); //< first member type in reflect_t is `unsigned id`, so less_unsigned_ptr works
|
|
|
|
#endif
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
2023-10-15 11:16:35 +00:00
|
|
|
reflect_t member_find(const char *T, const char *M) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
T = symbol_naked(T);
|
|
|
|
M = symbol_naked(M);
|
2023-10-11 19:01:06 +00:00
|
|
|
return *map_find(reflects, (intern(M)<<16)|intern(T));
|
|
|
|
}
|
|
|
|
void *member_findptr(void *obj, const char *T, const char *M) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
T = symbol_naked(T);
|
|
|
|
M = symbol_naked(M);
|
2023-10-11 19:01:06 +00:00
|
|
|
return (char*)obj + member_find(T,M).sz;
|
|
|
|
}
|
2023-10-21 09:18:13 +00:00
|
|
|
array(reflect_t)* members_find(const char *T) {
|
2023-10-16 15:15:59 +00:00
|
|
|
reflect_init();
|
2023-11-02 09:54:49 +00:00
|
|
|
T = symbol_naked(T);
|
2023-10-21 09:18:13 +00:00
|
|
|
return map_find(members, intern(T));
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
|
|
|
|
2023-10-23 13:25:03 +00:00
|
|
|
static
|
|
|
|
void ui_reflect_(const reflect_t *R, const char *filter, int mask) {
|
|
|
|
// debug:
|
|
|
|
// ui_label(va("name:%s info:'%s' id:%u objtype:%u sz:%u addr:%p parent:%u type:%s\n",
|
|
|
|
// R->name ? R->name : "", R->info ? R->info : "", R->id, R->objtype, R->sz, R->addr, R->parent, R->type ? R->type : ""));
|
2023-10-16 15:15:59 +00:00
|
|
|
|
2023-10-23 13:25:03 +00:00
|
|
|
if( mask == *R->info ) {
|
|
|
|
static __thread char *buf = 0;
|
|
|
|
if( buf ) *buf = '\0';
|
|
|
|
|
|
|
|
struct nk_context *ui_ctx = (struct nk_context *)ui_handle();
|
2023-11-01 11:24:16 +00:00
|
|
|
for ui_push_hspace(16) {
|
2023-10-23 13:25:03 +00:00
|
|
|
array(reflect_t) *T = map_find(members, intern(R->name));
|
2023-11-01 11:24:16 +00:00
|
|
|
/**/ if( T ) {ui_label(strcatf(&buf,"S struct %s@%s", R->name, R->info+1));
|
|
|
|
for each_array_ptr(*T, reflect_t, it)
|
|
|
|
if(strmatchi(it->name,filter)) {
|
|
|
|
if( !R->type && !strcmp(it->name,R->name) ) // avoid recursion
|
|
|
|
ui_label(strcatf(&buf,"M %s %s@%s", it->type, it->name, it->info+1));
|
|
|
|
else
|
|
|
|
ui_reflect_(it,filter,'M');
|
|
|
|
}
|
|
|
|
}
|
2023-10-23 13:25:03 +00:00
|
|
|
else if( R->addr ) ui_label(strcatf(&buf,"F func %s()@%s", R->name, R->info+1));
|
|
|
|
else if( !R->parent ) ui_label(strcatf(&buf,"E enum %s = %d@%s", R->name, R->sz, R->info+1));
|
|
|
|
else ui_label(strcatf(&buf,"M %s %s@%s", R->type, R->name, R->info+1));
|
|
|
|
}
|
2023-10-16 15:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:25:03 +00:00
|
|
|
API void *ui_handle();
|
|
|
|
int ui_reflect(const char *filter) {
|
|
|
|
if( !filter ) filter = "*";
|
|
|
|
|
|
|
|
int enabled = ui_enabled();
|
|
|
|
ui_disable();
|
|
|
|
|
|
|
|
// ENUMS, then FUNCTIONS, then STRUCTS
|
|
|
|
unsigned masks[] = { 'E', 'F', 'S' };
|
|
|
|
for( int i = 0; i < countof(masks); ++i )
|
|
|
|
for each_map_ptr(reflects, unsigned, k, reflect_t, R) {
|
|
|
|
if( strmatchi(R->name, filter)) {
|
|
|
|
ui_reflect_(R, filter, masks[i]);
|
2023-10-16 15:15:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 13:25:03 +00:00
|
|
|
if( enabled ) ui_enable();
|
|
|
|
return 0;
|
2023-10-16 15:15:59 +00:00
|
|
|
}
|
|
|
|
|
2023-10-11 19:01:06 +00:00
|
|
|
// -- tests
|
|
|
|
|
2023-10-16 20:07:29 +00:00
|
|
|
// type0 is reserved (no type)
|
|
|
|
// type1 reserved for objs
|
|
|
|
// type2 reserved for entities
|
|
|
|
// @todo: type3 and 4 likely reserved for components and systems??
|
|
|
|
// enum { OBJTYPE_vec3 = 0x03 };
|
2023-10-11 19:01:06 +00:00
|
|
|
|
|
|
|
AUTOTEST {
|
2023-10-16 20:07:29 +00:00
|
|
|
// register structs, enums and functions. with and without comments+tags
|
2023-10-11 19:01:06 +00:00
|
|
|
|
2023-10-16 20:07:29 +00:00
|
|
|
STRUCT( vec3, float, x );
|
|
|
|
STRUCT( vec3, float, y );
|
|
|
|
STRUCT( vec3, float, z, "Up" );
|
2023-10-11 19:01:06 +00:00
|
|
|
|
2023-10-16 20:07:29 +00:00
|
|
|
ENUM( IMAGE_RGB );
|
|
|
|
ENUM( TEXTURE_RGB, "3-channel Red+Green+Blue texture flag" );
|
|
|
|
ENUM( TEXTURE_RGBA, "4-channel Red+Green+Blue+Alpha texture flag" );
|
2023-10-11 19:01:06 +00:00
|
|
|
|
2023-10-16 20:07:29 +00:00
|
|
|
FUNCTION( puts );
|
|
|
|
FUNCTION( printf, "function that prints formatted text to stdout" );
|
2023-10-11 19:01:06 +00:00
|
|
|
|
|
|
|
// verify some reflected infos
|
|
|
|
|
|
|
|
test( function_find("puts") == puts );
|
|
|
|
test( function_find("printf") == printf );
|
|
|
|
|
2023-10-16 20:07:29 +00:00
|
|
|
test( enum_find("TEXTURE_RGB") == TEXTURE_RGB );
|
|
|
|
test( enum_find("TEXTURE_RGBA") == TEXTURE_RGBA );
|
2023-10-11 19:01:06 +00:00
|
|
|
|
|
|
|
// iterate reflected struct
|
2023-10-16 20:07:29 +00:00
|
|
|
for each_member("vec3", R) {
|
|
|
|
//printf("+%s vec3.%s (+%x) // %s\n", R->type, R->name, R->member_offset, R->info);
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|
|
|
|
|
2023-10-16 15:15:59 +00:00
|
|
|
// reflect_print("puts");
|
2023-10-16 20:07:29 +00:00
|
|
|
//reflect_print("TEXTURE_RGBA");
|
|
|
|
//reflect_print("vec3");
|
2023-10-16 15:15:59 +00:00
|
|
|
|
|
|
|
// reflect_dump("*");
|
2023-10-11 19:01:06 +00:00
|
|
|
}
|