1 #ifndef MUTATORS_BASE_H
2 #define MUTATORS_BASE_H
4 const int CBC_ORDER_FIRST = 1;
5 const int CBC_ORDER_LAST = 2;
6 const int CBC_ORDER_EXCLUSIVE = 3;
7 const int CBC_ORDER_ANY = 4;
9 bool CallbackChain_ReturnValue; // read-only field of the current return value
12 * Callbacks may be added to zero or more callback chains.
14 CLASS(Callback, Object)
16 * a callback function is like this:
23 ATTRIB(Callback, cbc_func, bool(), func_null)
24 CONSTRUCTOR(Callback, bool() func) {
32 * Callback chains contain zero or more callbacks.
34 CLASS(CallbackChain, Object)
35 CLASS(CallbackNode, Object)
36 ATTRIB(CallbackNode, cbc, Callback, NULL)
37 ATTRIB(CallbackNode, cbc_next, CallbackNode, NULL)
38 ATTRIB(CallbackNode, cbc_order, int, 0)
39 CONSTRUCTOR(CallbackNode, Callback it, int order) {
40 CONSTRUCT(CallbackNode);
42 this.cbc_order = order;
45 ENDCLASS(CallbackNode)
47 ATTRIB(CallbackChain, cbc_next, CallbackNode, NULL)
48 ATTRIB(CallbackChain, cbc_order, int, 0)
49 CONSTRUCTOR(CallbackChain, string name) {
50 CONSTRUCT(CallbackChain);
55 bool CallbackChain_Add(CallbackChain this, Callback cb, int order)
57 if (order & CBC_ORDER_FIRST) {
58 if (order & CBC_ORDER_LAST)
59 if (this.cbc_order & CBC_ORDER_ANY)
61 if (this.cbc_order & CBC_ORDER_FIRST)
63 } else if (order & CBC_ORDER_LAST) {
64 if (this.cbc_order & CBC_ORDER_LAST)
67 entity node = NEW(CallbackNode, cb, order);
68 if (order & CBC_ORDER_FIRST) {
69 node.cbc_next = this.cbc_next;
71 } else if (order & CBC_ORDER_LAST) {
72 CallbackNode prev = NULL, it = this.cbc_next;
73 while (it) { prev = it, it = it.cbc_next; }
74 if (prev) prev.cbc_next = node;
75 else this.cbc_next = node;
77 // by default we execute last, but before a possible CBC_ORDER_LAST callback
78 CallbackNode prev = NULL, it = this.cbc_next;
79 while (it && !(it.cbc_order & CBC_ORDER_LAST)) { prev = it, it = it.cbc_next; }
81 if (prev) prev.cbc_next = node;
82 else this.cbc_next = node;
84 this.cbc_order |= (order | CBC_ORDER_ANY);
87 int CallbackChain_Remove(CallbackChain this, Callback cb)
90 for (Callback prev = NULL, it = this.cbc_next; it; prev = it, it = it.cbc_next) {
92 // remove it from the chain
93 Callback next = it.cbc_next;
94 if (prev) prev.cbc_next = next;
95 else this.cbc_next = next;
98 // it is now something we want to keep
99 order |= (it.cbc_order & CBC_ORDER_ANY);
101 this.cbc_order = order;
104 bool CallbackChain_Call(CallbackChain this)
107 for (Callback it = this.cbc_next; it; it = it.cbc_next) {
108 CallbackChain_ReturnValue = r;
109 r |= it.cbc.cbc_func();
111 return r; // callbacks return an error status, so 0 is default return value
113 ENDCLASS(CallbackChain)
115 #define _MUTATOR_HANDLE_NOP(type, id)
116 #define _MUTATOR_HANDLE_PARAMS(type, id) , type in_##id
117 #define _MUTATOR_HANDLE_PREPARE(type, id) id = in_##id;
118 #define _MUTATOR_HANDLE_PUSHTMP(type, id) type tmp_##id = id;
119 #define _MUTATOR_HANDLE_PUSHOUT(type, id) type out_##id = id;
120 #define _MUTATOR_HANDLE_POPTMP(type, id) id = tmp_##id;
121 #define _MUTATOR_HANDLE_POPOUT(type, id) id = out_##id;
123 #define _MUTATOR_HOOKABLE(id, ...) CallbackChain HOOK_##id; bool __Mutator_Send_##id(__VA_ARGS__)
124 #define MUTATOR_HOOKABLE(id, params) \
125 _MUTATOR_HOOKABLE(id, int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \
126 params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \
127 params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \
128 bool ret = CallbackChain_Call(HOOK_##id); \
129 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_PUSHOUT) \
130 params(_MUTATOR_HANDLE_POPTMP, _MUTATOR_HANDLE_NOP) \
131 params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_POPOUT) \
134 STATIC_INIT(HOOK_##id) { HOOK_##id = NEW(CallbackChain, #id); }
135 #define MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__)
143 typedef bool(int) mutatorfunc_t;
145 CLASS(Mutator, Object)
146 ATTRIB(Mutator, mutatorname, string, string_null)
147 ATTRIB(Mutator, mutatorfunc, mutatorfunc_t, func_null)
148 CONSTRUCTOR(Mutator, string name, mutatorfunc_t func) {
150 this.mutatorname = name;
151 this.mutatorfunc = func;
156 const int MAX_MUTATORS = 15;
157 Mutator loaded_mutators[MAX_MUTATORS];
159 bool Mutator_Add(Mutator mut)
162 for (int i = 0; i < MAX_MUTATORS; ++i) {
163 if (loaded_mutators[i] == mut)
164 return true; // already added
165 if (!(loaded_mutators[i]))
169 backtrace("WARNING: too many mutators, cannot add any more\n");
172 loaded_mutators[j] = mut;
173 mutatorfunc_t func = mut.mutatorfunc;
174 if (!func(MUTATOR_ADDING)) {
178 backtrace("WARNING: when adding mutator: adding failed, rolling back\n");
179 if (func(MUTATOR_ROLLING_BACK)) {
181 error("WARNING: when adding mutator: rolling back failed");
186 void Mutator_Remove(Mutator mut)
189 for (i = 0; i < MAX_MUTATORS; ++i)
190 if (loaded_mutators[i] == mut)
192 if (i >= MAX_MUTATORS) {
193 backtrace("WARNING: removing not-added mutator\n");
196 loaded_mutators[i] = NULL;
197 mutatorfunc_t func = mut.mutatorfunc;
198 if (func(MUTATOR_REMOVING)) {
200 error("Mutator_Remove: removing mutator failed");
204 #define MUTATOR_DECLARATION(name) \
205 Mutator MUTATOR_##name
206 #define MUTATOR_DEFINITION(name) \
207 bool MUTATORFUNCTION_##name(int mode); \
208 STATIC_INIT(MUTATOR_##name) { MUTATOR_##name = NEW(Mutator, #name, MUTATORFUNCTION_##name); } \
209 bool MUTATORFUNCTION_##name(int mode)
210 #define MUTATOR_ONADD if (mode == MUTATOR_ADDING)
211 #define MUTATOR_ONREMOVE if (mode == MUTATOR_REMOVING)
212 #define MUTATOR_ONROLLBACK_OR_REMOVE if (mode == MUTATOR_REMOVING || mode == MUTATOR_ROLLING_BACK)
213 #define MUTATOR_ADD(name) Mutator_Add(MUTATOR_##name)
214 #define MUTATOR_REMOVE(name) Mutator_Remove(MUTATOR_##name)
215 #define MUTATOR_RETURNVALUE CallbackChain_ReturnValue
216 #define MUTATOR_HOOKFUNCTION(name) \
217 bool HOOKFUNCTION_##name(); \
218 Callback CALLBACK_##name; STATIC_INIT(CALLBACK_##name) { CALLBACK_##name = NEW(Callback, HOOKFUNCTION_##name); } \
219 bool HOOKFUNCTION_##name()
220 #define MUTATOR_HOOK(cb, func, order) do { \
222 if (!CallbackChain_Add(HOOK_##cb, CALLBACK_##func, order)) { \
223 print("HOOK FAILED: ", #cb, ":", #func, "\n"); \
227 MUTATOR_ONROLLBACK_OR_REMOVE { \
228 CallbackChain_Remove(HOOK_##cb, CALLBACK_##func); \