#ifndef REGISTRY_H #define REGISTRY_H #include "oo.qh" #define REGISTER_INIT(ns, id) [[accumulate]] void Register_##ns##_##id##_init(entity this) #define REGISTER_INIT_POST(ns, id) [[accumulate]] void Register_##ns##_##id##_init_post(entity this) #define REGISTRY(id, max) \ void Register##id() {} \ const int id##_MAX = max; \ noref entity id[id##_MAX], id##_first, id##_last; \ int id##_COUNT; /** * Register a new entity with a global constructor. * Must be followed by a semicolon or a function body with a `this` parameter. * Wrapper macros may perform actions after user initialization like so: * #define REGISTER_FOO(id) \ * REGISTER(RegisterFoos, FOO, FOOS, id, m_id, NEW(Foo)); \ * REGISTER_INIT_POST(FOO, id) { \ * print("Registering foo #", this.m_id + 1, "\n"); \ * } \ * REGISTER_INIT(FOO, id) * * Don't forget to forward declare `initfunc` and call `REGISTER_REGISTRY`: * void RegisterFoos(); * REGISTER_REGISTRY(RegisterFoos) * * @param initfunc The global constructor to accumulate into * @param ns Short for namespace, prefix for each global (ns##_##id) * @param array The array to add each entity to. Also requires `array##_first` and `array##_last` to be defined * @param id The identifier of the current entity being registered * @param fld The field to store the current count into * @param inst An expression to create a new instance, invoked for every registration */ #define REGISTER(initfunc, ns, array, id, fld, inst) \ entity ns##_##id; \ REGISTER_INIT(ns, id) { } \ REGISTER_INIT_POST(ns, id) { } \ .entity enemy; /* internal next pointer */ \ void Register_##ns##_##id() { \ if (array##_COUNT >= array##_MAX) LOG_FATALF("Registry capacity exceeded (%s)", ftos(array##_MAX)); \ entity this = inst; \ ns##_##id = this; \ this.fld = array##_COUNT; \ array[array##_COUNT++] = this; \ if (!array##_first) array##_first = this; \ if ( array##_last) array##_last.enemy = this; \ array##_last = this; \ Register_##ns##_##id##_init(this); \ Register_##ns##_##id##_init_post(this); \ } \ ACCUMULATE_FUNCTION(initfunc, Register_##ns##_##id) \ REGISTER_INIT(ns, id) #define REGISTRY_SORT(id, field, skip) \ void _REGISTRY_SWAP_##id(int i, int j, entity pass) { \ i += skip; j += skip; \ entity e = id[i]; \ id[i] = id[j]; \ id[j] = e; \ } \ float _REGISTRY_CMP_##id(int i, int j, entity pass) { \ i += skip; j += skip; \ string a = id[i].field; \ string b = id[j].field; \ return strcasecmp(a, b); \ } \ STATIC_INIT(Registry_sort_##id) { \ heapsort(id##_COUNT - (skip), _REGISTRY_SWAP_##id, _REGISTRY_CMP_##id, NULL); \ } #endif