Real Time Open Sound Control librtosc
port-sugar.h
1 /*
2  * Copyright (c) 2016 Mark McCurry
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 
25 #include <assert.h>
26 #include <type_traits>
27 #include <cstring>
28 
29 #ifndef RTOSC_PORT_SUGAR
30 #define RTOSC_PORT_SUGAR
31 
32 //Hack to workaround old incomplete decltype implementations
33 #ifdef __GNUC__
34 #ifndef __clang__
35 #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 7)
36 template<typename T>
37 struct rtosc_hack_decltype_t
38 {
39  typedef T type;
40 };
41 
42 #define decltype(expr) rtosc_hack_decltype_t<decltype(expr)>::type
43 #endif
44 #endif
45 #endif
46 
47 //General macro utilities
48 #define STRINGIFY2(a) #a
49 #define STRINGIFY(a) STRINGIFY2(a)
50 
51 //Helper for documenting varargs
52 #define IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,N, ...) N
53 #define LAST_IMP(...) IMPL(__VA_ARGS__,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,0,0)
54 #define DOC_IMP12(a,b,c,d,e,f,g,h,i,j,k,l) a b c d e f g h i j k rDoc(l)
55 #define DOC_IMP11(a,b,c,d,e,f,g,h,i,j,k) a b c d e f g h i j rDoc(k)
56 #define DOC_IMP10(a,b,c,d,e,f,g,h,i,j) a b c d e f g h i rDoc(j)
57 #define DOC_IMP9(a,b,c,d,e,f,g,h,i) a b c d e f g h rDoc(i)
58 #define DOC_IMP8(a,b,c,d,e,f,g,h) a b c d e f g rDoc(h)
59 #define DOC_IMP7(a,b,c,d,e,f,g) a b c d e f rDoc(g)
60 #define DOC_IMP6(a,b,c,d,e,f) a b c d e rDoc(f)
61 #define DOC_IMP5(a,b,c,d,e) a b c d rDoc(e)
62 #define DOC_IMP4(a,b,c,d) a b c rDoc(d)
63 #define DOC_IMP3(a,b,c) a b rDoc(c)
64 #define DOC_IMP2(a,b) a rDoc(b)
65 #define DOC_IMP1(a) rDoc(a)
66 #define DOC_IMP0() YOU_MUST_DOCUMENT_YOUR_PORTS
67 #define DOC_IMP(count, ...) DOC_IMP ##count(__VA_ARGS__)
68 #define DOC_I(count, ...) DOC_IMP(count,__VA_ARGS__)
69 #define DOC(...) DOC_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)
70 
71 
72 #define rINC(x) rINC_ ## x
73 #define rINC_0 1
74 #define rINC_1 2
75 #define rINC_2 3
76 #define rINC_3 4
77 #define rINC_4 5
78 #define rINC_5 6
79 #define rINC_6 7
80 #define rINC_7 8
81 #define rINC_8 9
82 #define rINC_9 10
83 #define rINC_10 11
84 #define rINC_11 12
85 #define rINC_12 13
86 #define rINC_13 14
87 #define rINC_14 15
88 #define rINC_15 16
89 
90 //Helper for applying macro on varargs
91 //arguments: counting offset, macro, macro args
92 #define MAC_EACH_0(o,m,d,x, ...) INSUFFICIENT_ARGUMENTS_PROVIDED_TO_MAC_EACH
93 #define MAC_EACH_1(o,m,d,x, ...) m(o,d,x)
94 #define MAC_EACH_2(o,m,d,x, ...) m(o,d,x) MAC_EACH_1(rINC(o),m,d, __VA_ARGS__)
95 #define MAC_EACH_3(o,m,d,x, ...) m(o,d,x) MAC_EACH_2(rINC(o),m,d, __VA_ARGS__)
96 #define MAC_EACH_4(o,m,d,x, ...) m(o,d,x) MAC_EACH_3(rINC(o),m,d, __VA_ARGS__)
97 #define MAC_EACH_5(o,m,d,x, ...) m(o,d,x) MAC_EACH_4(rINC(o),m,d, __VA_ARGS__)
98 #define MAC_EACH_6(o,m,d,x, ...) m(o,d,x) MAC_EACH_5(rINC(o),m,d, __VA_ARGS__)
99 #define MAC_EACH_7(o,m,d,x, ...) m(o,d,x) MAC_EACH_6(rINC(o),m,d, __VA_ARGS__)
100 #define MAC_EACH_8(o,m,d,x, ...) m(o,d,x) MAC_EACH_7(rINC(o),m,d, __VA_ARGS__)
101 #define MAC_EACH_9(o,m,d,x, ...) m(o,d,x) MAC_EACH_8(rINC(o),m,d, __VA_ARGS__)
102 #define MAC_EACH_10(o,m,d,x, ...) m(o,d,x) MAC_EACH_9(rINC(o),m,d, __VA_ARGS__)
103 #define MAC_EACH_11(o,m,d,x, ...) m(o,d,x) MAC_EACH_10(rINC(o),m,d, __VA_ARGS__)
104 #define MAC_EACH_12(o,m,d,x, ...) m(o,d,x) MAC_EACH_11(rINC(o),m,d, __VA_ARGS__)
105 #define MAC_EACH_13(o,m,d,x, ...) m(o,d,x) MAC_EACH_12(rINC(o),m,d, __VA_ARGS__)
106 #define MAC_EACH_14(o,m,d,x, ...) m(o,d,x) MAC_EACH_13(rINC(o),m,d, __VA_ARGS__)
107 #define MAC_EACH_15(o,m,d,x, ...) m(o,d,x) MAC_EACH_14(rINC(o),m,d, __VA_ARGS__)
108 #define MAC_EACH_16(o,m,d,x, ...) m(o,d,x) MAC_EACH_15(rINC(o),m,d, __VA_ARGS__)
109 
110 #define MAC_EACH_IMP(off, mac, data, count, ...) \
111  MAC_EACH_ ##count(off, mac, data, __VA_ARGS__)
112 #define MAC_EACH_I(off, mac, data, count, ...) \
113  MAC_EACH_IMP(off, mac, data, count, __VA_ARGS__)
114 #define MAC_EACH_OFF(off, mac, data, ...) \
115  MAC_EACH_I(off, mac, data, LAST_IMP(__VA_ARGS__), __VA_ARGS__)
116 #define MAC_EACH(mac, data, ...) MAC_EACH_OFF(0, mac, data, __VA_ARGS__)
117 
118 // 1 2 3 4 5 6 7 8 910111213141516
119 #define OPTIONS_IMP16(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \
120  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
121  rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
122  rOpt(14,o)rOpt(15,p)
123 #define OPTIONS_IMP15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \
124  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
125  rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)\
126  rOpt(14,o)
127 #define OPTIONS_IMP14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
128  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
129  rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)rOpt(13,n)
130 #define OPTIONS_IMP13(a,b,c,d,e,f,g,h,i,j,k,l,m) \
131  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
132  rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)rOpt(12,m)
133 #define OPTIONS_IMP12(a,b,c,d,e,f,g,h,i,j,k,l) \
134  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
135  rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)rOpt(11,l)
136 #define OPTIONS_IMP11(a,b,c,d,e,f,g,h,i,j,k) \
137  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
138  rOpt(7,h) rOpt(8,i) rOpt(9,j) rOpt(10,k)
139 #define OPTIONS_IMP10(a,b,c,d,e,f,g,h,i,j) \
140  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
141  rOpt(7,h) rOpt(8,i) rOpt(9,j)
142 #define OPTIONS_IMP9(a,b,c,d,e,f,g,h,i) \
143  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
144  rOpt(7,h) rOpt(8,i)
145 #define OPTIONS_IMP8(a,b,c,d,e,f,g,h) \
146  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g) \
147  rOpt(7,h)
148 #define OPTIONS_IMP7(a,b,c,d,e,f,g) \
149  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f) rOpt(6,g)
150 #define OPTIONS_IMP6(a,b,c,d,e,f) \
151  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e) rOpt(5,f)
152 #define OPTIONS_IMP5(a,b,c,d,e) \
153  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d) rOpt(4,e)
154 #define OPTIONS_IMP4(a,b,c,d) \
155  rOpt(0,a) rOpt(1,b) rOpt(2,c) rOpt(3,d)
156 #define OPTIONS_IMP3(a,b,c) \
157  rOpt(0,a) rOpt(1,b) rOpt(2,c)
158 #define OPTIONS_IMP2(a,b) \
159  rOpt(0,a) rOpt(1,b)
160 #define OPTIONS_IMP1(a) \
161  rOpt(0,a)
162 #define OPTIONS_IMP0() YOU_MUST_PROVIDE_OPTIONS
163 #define OPTIONS_IMP(count, ...) OPTIONS_IMP ##count(__VA_ARGS__)
164 #define OPTIONS_I(count, ...) OPTIONS_IMP(count, __VA_ARGS__)
165 #define OPTIONS(...) OPTIONS_I(LAST_IMP(__VA_ARGS__), __VA_ARGS__)
166 
167 //Additional Change Callback (after parameters have been changed)
168 //This can be used to queue up interpolation or parameter regen
169 #define rChangeCb
170 
171 //Normal parameters
172 #define rParam(name, ...) \
173  {STRINGIFY(name) "::c", rProp(parameter) rMap(min, 0) rMap(max, 127) DOC(__VA_ARGS__), NULL, rParamCb(name)}
174 #define rParamF(name, ...) \
175  {STRINGIFY(name) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamFCb(name)}
176 #define rParamI(name, ...) \
177  {STRINGIFY(name) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rParamICb(name)}
178 #define rToggle(name, ...) \
179  {STRINGIFY(name) "::T:F",rProp(parameter) DOC(__VA_ARGS__), NULL, rToggleCb(name)}
180 #define rOption(name, ...) \
181  {STRINGIFY(name) "::i:c:S",rProp(parameter) rProp(enumerated) DOC(__VA_ARGS__), NULL, rOptionCb(name)}
182 
183 //Array operators
184 #define rArrayF(name, length, ...) \
185 {STRINGIFY(name) "#" STRINGIFY(length) "::f", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayFCb(name)}
186 #define rArrayT(name, length, ...) \
187 {STRINGIFY(name) "#" STRINGIFY(length) "::T:F", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayTCb(name)}
188 #define rArrayI(name, length, ...) \
189 {STRINGIFY(name) "#" STRINGIFY(length) "::i", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayICb(name)}
190 #define rArrayOption(name, length, ...) \
191 {STRINGIFY(name) "#" STRINGIFY(length) "::i:c:S", rProp(parameter) DOC(__VA_ARGS__), NULL, rArrayOptionCb(name)}
192 #define rArray rArrayI
193 
194 //Method callback Actions
195 #define rAction(name, ...) \
196 {STRINGIFY(name) ":", DOC(__VA_ARGS__), NULL, rActionCb(name)}
197 #define rActioni(name, ...) \
198 {STRINGIFY(name) ":i", DOC(__VA_ARGS__), NULL, rActioniCb(name)}
199 
200 
201 //Alias operators
202 #define rParams(name, length, ...) \
203 rArray(name, length, __VA_ARGS__), \
204 {STRINGIFY(name) ":", rProp(alias) rDoc("get all data from aliased array"), NULL, rParamsCb(name, length)}
205 
206 
207 template<class T> constexpr T spice(T*t) {return *t;}
208 
209 //Recursion [two ports in one for pointer manipulation]
210 #define rRecur(name, ...) \
211  {STRINGIFY(name) "/", DOC(__VA_ARGS__), &decltype(rObject::name)::ports, rRecurCb(name)}, \
212  {STRINGIFY(name) ":", rProp(internal) rDoc("get obj pointer"), NULL, rRecurPtrCb(name)}
213 
214 #define rRecurp(name, ...) \
215  {STRINGIFY(name) "/", DOC(__VA_ARGS__), \
216  &decltype(spice(rObject::name))::ports, \
217  rRecurpCb(name)}
218 
219 #define rRecurs(name, length, ...) \
220  {STRINGIFY(name) "#" STRINGIFY(length)"/", DOC(__VA_ARGS__), \
221  &decltype(spice(&rObject::name[0]))::ports, \
222  rRecursCb(name, length)}
223 
224 //Technically this is a pointer pointer method...
225 #define rRecursp(name, length, ...) \
226  {STRINGIFY(name)"#" STRINGIFY(length) "/", DOC(__VA_ARGS__), \
227  &decltype(spice(rObject::name[0]))::ports, \
228  rRecurspCb(name)}
229 
230 //{STRINGIFY(name) ":", rProp(internal), NULL, rRecurPtrCb(name)}
231 
232 //let this recurring parameter depend on another port
233 //the path of the other port must be at the current level or one level above
234 #define rEnabledBy(portname) rMap(enabled by, portname)
235 #define rEnabledByCondition(cond_name) rEnabledBy(cond_name)
236 #define rEnabledCondition(cond_name, condition) \
237  {STRINGIFY(cond_name) ":", rProp(internal), NULL, rEnabledIfCb(condition)}
238 #define rEnabledIfCb(condition) rBOIL_BEGIN \
239  assert(!rtosc_narguments(msg)); \
240  data.reply(loc, (condition)?"T":"F"); \
241  rBOIL_END \
242 
243 #define rSelf(type, ...) \
244 {"self:", rProp(internal) rMap(class, type) __VA_ARGS__ rDoc("port metadata"), 0, \
245  [](const char *, rtosc::RtData &d){ \
246  d.reply(d.loc, "b", sizeof(d.obj), &d.obj);}}\
247 
248 //Misc
249 #define rDummy(name, ...) {STRINGIFY(name), rProp(dummy), NULL, [](msg_t, rtosc::RtData &){}}
250 #define rString(name, len, ...) \
251  {STRINGIFY(name) "::s", rMap(length, len) rProp(parameter) DOC(__VA_ARGS__), NULL, rStringCb(name,len)}
252 
253 //General property operators
254 #define rMap(name, value) ":" STRINGIFY(name) "\0=" STRINGIFY(value) "\0"
255 #define rProp(name) ":" STRINGIFY(name) "\0"
256 
257 //Scaling property
258 //This property describes the variable's input scale, which is in most cases
259 //(not always) equal to the perception. Thus, if 0 is in the input scale, and
260 //has no special meaning, rLinear shall be used.
261 #define rLinear(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, linear)
262 #define rLog(min_, max_) rMap(min, min_) rMap(max, max_) rMap(scale, logarithmic)
263 
264 //Special values
265 #define rSpecial(doc) ":special\0" STRINGIFY(doc) "\0"
266 #define rCentered ":centered\0"
267 
268 //Default values
269 #define rDefault(default_value_) rMap(default, default_value_)
270 #define rDefaultId(default_value_) ":default\0=\"" STRINGIFY(default_value_) "\"S\0"
271 //#define rDefaultArr(default_value_, idx_) rMap(default[idx_], default_value_)
272 #define rPreset(no, default_value) \
273  ":default " STRINGIFY(no) "\0=" STRINGIFY(default_value) "\0"
274 #define _rPreset(no, data, default_value) rPreset(no, default_value)
275 #define rPresetsAt(offs, ...) MAC_EACH_OFF(offs, _rPreset, offs, __VA_ARGS__)
276 #define _rPreset2(offs, data, preset_no) rPreset(preset_no, data)
277 #define rPresetAtMulti(val, ...) MAC_EACH_OFF(0, _rPreset2, val, __VA_ARGS__)
278 #define rPresets(...) rPresetsAt(0, __VA_ARGS__)
279 #define rDefaultDepends(dep_path_) rMap(default depends, dep_path_)
280 #define rDefaultMissing "" // macro to denote yet missing default values
281 #define rNoDefaults ":no defaults\0"
283 
284 //Misc properties
285 #define rDoc(doc) ":documentation\0=" doc "\0"
286 #define rOpt(numeric,symbolic) rMap(map numeric, symbolic)
287 #define rOptions(...) OPTIONS(__VA_ARGS__)
288 
289 //Zest Metadata
290 #define rShort(name) ":shortname\0=" name "\0"
291 
292 
293 //Callback Implementations
294 #define rBOIL_BEGIN [](const char *msg, rtosc::RtData &data) { \
295  (void) msg; (void) data; \
296  rObject *obj = (rObject*) data.obj;(void) obj; \
297  const char *args = rtosc_argument_string(msg); (void) args;\
298  const char *loc = data.loc; (void) loc;\
299  auto prop = data.port->meta(); (void) prop;
300 
301 #define rBOIL_END }
302 
303 #define rLIMIT(var, convert) \
304  if(prop["min"] && var < (decltype(var)) convert(prop["min"])) \
305  var = convert(prop["min"]);\
306  if(prop["max"] && var > (decltype(var)) convert(prop["max"])) \
307  var = convert(prop["max"]);
308 
309 #define rTYPE(n) decltype(obj->n)
310 
311 //#define rAPPLY(n,t) if(obj->n != var) data.reply("/undo_change", "s" #t #t, data.loc, obj->n, var); obj->n = var;
312 #define rCAPPLY(getcode, t, setcode) if(getcode != var) data.reply("/undo_change", "s" #t #t, data.loc, getcode, var); setcode;
313 #define rAPPLY(n,t) rCAPPLY(obj->n, t, obj->n = var)
314 
315 #define rParamCb(name) rBOIL_BEGIN \
316  if(!strcmp("", args)) {\
317  data.reply(loc, "c", obj->name); \
318  } else { \
319  rTYPE(name) var = rtosc_argument(msg, 0).i; \
320  rLIMIT(var, atoi) \
321  rAPPLY(name, c) \
322  data.broadcast(loc, "c", obj->name);\
323  rChangeCb \
324  } rBOIL_END
325 
326 #define rParamFCb(name) rBOIL_BEGIN \
327  if(!strcmp("", args)) {\
328  data.reply(loc, "f", obj->name); \
329  } else { \
330  rTYPE(name) var = rtosc_argument(msg, 0).f; \
331  rLIMIT(var, atof) \
332  rAPPLY(name, f) \
333  data.broadcast(loc, "f", obj->name);\
334  rChangeCb \
335  } rBOIL_END
336 
337 #define rParamICb(name) rBOIL_BEGIN \
338  if(!strcmp("", args)) {\
339  data.reply(loc, "i", obj->name); \
340  } else { \
341  rTYPE(name) var = rtosc_argument(msg, 0).i; \
342  rLIMIT(var, atoi) \
343  rAPPLY(name, i) \
344  data.broadcast(loc, "i", obj->name);\
345  rChangeCb \
346  } rBOIL_END
347 
348 #define rCOptionCb_(getcode, setcode) { \
349  if(!strcmp("", args)) {\
350  data.reply(loc, "i", getcode); \
351  } else if(!strcmp("s", args) || !strcmp("S", args)) { \
352  auto var = \
353  enum_key(prop, rtosc_argument(msg, 0).s); \
354  /* make sure we have no out-of-bound options */ \
355  assert(!prop["min"] || \
356  var >= atoi(prop["min"])); \
357  assert(!prop["max"] || \
358  var <= atoi(prop["max"])); \
359  rCAPPLY(getcode, i, setcode) \
360  data.broadcast(loc, "i", getcode); \
361  rChangeCb \
362  } else {\
363  auto var = \
364  rtosc_argument(msg, 0).i; \
365  rLIMIT(var, atoi) \
366  rCAPPLY(getcode, i, setcode) \
367  data.broadcast(loc, rtosc_argument_string(msg), getcode);\
368  rChangeCb \
369  } \
370  }
371 
372 #define rOptionCb_(name) rCOptionCb_(obj->name, obj->name = var)
373 
374 #define rOptionCb(name) rBOIL_BEGIN \
375  rOptionCb_(name) \
376  rBOIL_END
377 
378 #define rCOptionCb(getcode, setcode) rBOIL_BEGIN \
379  rCOptionCb_(getcode, setcode) \
380  rBOIL_END
381 
382 
383 #define rToggleCb(name) rBOIL_BEGIN \
384  if(!strcmp("", args)) {\
385  data.reply(loc, obj->name ? "T" : "F"); \
386  } else { \
387  if(obj->name != rtosc_argument(msg, 0).T) { \
388  data.broadcast(loc, args);\
389  obj->name = rtosc_argument(msg, 0).T; \
390  rChangeCb \
391  } \
392  } rBOIL_END
393 
394 #define SNIP \
395  while(*msg && *msg!='/') ++msg; \
396  msg = *msg ? msg+1 : msg;
397 
398 #define rRecurCb(name) rBOIL_BEGIN \
399  data.obj = &obj->name; \
400  SNIP \
401  decltype(obj->name)::ports.dispatch(msg, data); \
402  rBOIL_END
403 
404 #define rRecurPtrCb(name) rBOIL_BEGIN \
405  void *ptr = &obj->name; \
406  data.reply(loc, "b", sizeof(void*), &ptr); \
407  rBOIL_END
408 
409 #define rRecurpCb(name) rBOIL_BEGIN \
410  data.obj = obj->name; \
411  if(obj->name == NULL) return; \
412  SNIP \
413  decltype(spice(rObject::name))::ports.dispatch(msg, data); \
414  rBOIL_END
415 
416 #define rRecursCb(name, length) rBOILS_BEGIN \
417  data.obj = &obj->name[idx]; \
418  SNIP \
419  decltype(spice(rObject::name))::ports.dispatch(msg, data); \
420  rBOILS_END
421 
422 #define rRecurspCb(name) rBOILS_BEGIN \
423  data.obj = obj->name[idx]; \
424  SNIP \
425  decltype(spice(rObject::name[0]))::ports.dispatch(msg, data); \
426  rBOILS_END
427 
428 #define rActionCb(name) rBOIL_BEGIN obj->name(); rBOIL_END
429 #define rActioniCb(name) rBOIL_BEGIN \
430  obj->name(rtosc_argument(msg,0).i); rBOIL_END
431 
432 //Array ops
433 
434 #define rBOILS_BEGIN rBOIL_BEGIN \
435  const char *mm = msg; \
436  while(*mm && !isdigit(*mm)) ++mm; \
437  unsigned idx = atoi(mm);
438 
439 #define rBOILS_END rBOIL_END
440 
441 
442 #define rArrayFCb(name) rBOILS_BEGIN \
443  if(!strcmp("", args)) {\
444  data.reply(loc, "f", obj->name[idx]); \
445  } else { \
446  float var = rtosc_argument(msg, 0).f; \
447  rLIMIT(var, atof) \
448  rAPPLY(name[idx], f) \
449  data.broadcast(loc, "f", obj->name[idx]);\
450  } rBOILS_END
451 
452 #define rArrayTCb(name) rBOILS_BEGIN \
453  if(!strcmp("", args)) {\
454  data.reply(loc, obj->name[idx] ? "T" : "F"); \
455  } else { \
456  if(obj->name[idx] != rtosc_argument(msg, 0).T) { \
457  data.broadcast(loc, args);\
458  rChangeCb \
459  } \
460  obj->name[idx] = rtosc_argument(msg, 0).T; \
461  } rBOILS_END
462 
463 #define rArrayTCbMember(name, member) rBOILS_BEGIN \
464  if(!strcmp("", args)) {\
465  data.reply(loc, obj->name[idx].member ? "T" : "F"); \
466  } else { \
467  if(obj->name[idx].member != rtosc_argument(msg, 0).T) { \
468  data.broadcast(loc, args);\
469  rChangeCb \
470  } \
471  obj->name[idx].member = rtosc_argument(msg, 0).T; \
472  } rBOILS_END
473 
474 
475 #define rArrayICb(name) rBOILS_BEGIN \
476  if(!strcmp("", args)) {\
477  data.reply(loc, "i", obj->name[idx]); \
478  } else { \
479  char var = rtosc_argument(msg, 0).i; \
480  rLIMIT(var, atoi) \
481  rAPPLY(name[idx], i) \
482  data.broadcast(loc, "i", obj->name[idx]);\
483  rChangeCb \
484  } rBOILS_END
485 
486 
487 #define rArrayOptionCb(name) rBOILS_BEGIN \
488  rOptionCb_(name[idx]) \
489  rBOILS_END
490 
491 #define rParamsCb(name, length) rBOIL_BEGIN \
492  data.reply(loc, "b", length, obj->name); rBOIL_END
493 
494 #define rStringCb(name, length) rBOIL_BEGIN \
495  if(!strcmp("", args)) {\
496  data.reply(loc, "s", obj->name); \
497  } else { \
498  strncpy(obj->name, rtosc_argument(msg, 0).s, length-1); \
499  obj->name[length-1] = '\0'; \
500  data.broadcast(loc, "s", obj->name);\
501  rChangeCb \
502  } rBOIL_END
503 
504 
505 #endif