Hi list,
Some time ago I wrote an external for generating euclidean rhythms. It worked in pd-extended (0.43.4), which I was using at the time. Now I've moved over to vanilla 0.47.1, and when I try to instantiate it, I get the following error:
class euclid: sorry: only 5 args typechecked; use A_GIMME objectmaker_euclid: only 5 arguments are typecheckable; use A_GIMME Bad arguments for message 'euclid' to object 'objectmaker'
So I tried what it suggests, with A_GIMME, 0, and now I can instantiate it, but it doesn't work. As soon as I feed it a bang, Pd crashes with terminal output "Floating point exception".
Code is attached. Any suggestions? What's this typechecking/A_GIMME business about anyway?
_chris
On 2017-02-06 07:42, Chris Chronopoulos wrote:
Code is attached. Any suggestions? What's this typechecking/A_GIMME
Pd has special, "optimized" functions to check the types of arguments.
basically, if you have a callback function (and from a pure "C" perspective, the class-methods of an objects are callback functions), you must correctly call that function. that is: if the callback function expects a symbols and then a float ("void foo_mess(t_myobject*x, t_symbol*s, t_float f)"), you must actually call it with a symbol and then a float ("foo_mess(x, s2, 2.3)"), to ensure that the compiler will put the correct bits at the right positions.
now in Pd, an external can wish for "any" function signature of the callback it wishes for, but it is the task of Pd (core) to actually call that callback function correctly. since "any" signature is pretty broad and it is tedious to write special callback callers for all the possibilities, it offers a (slightly less convenient) generic solution for "non-standard" callbacks: pass a list of atoms (aka A_GIMME) messages with more that 5 atoms are "non-standard", and therefore you must use A_GIMME.
the function signature of an A_GIMME is: int argc, t_atom*argv you then can iterate over the <argc> atoms in the <argv> array, and use them whoever you please (mainly: check that each element is of correct type, and use atom_getfloat() resp atom_getsymbol() to acccess the values).
i suspect (without having looked at your code), that you were simply changing the type to A_GIMME when registering the callback (in the class_addmothod() function), but then didn't bother to change the function signature of the actual callback function.
so if your callback signature is: void foo_mess(t_myobject*x, t_symbol*s, t_float f); and you register it with class_addmethod(x, (t_method)foo_mess, gensym("foo"), A_GIMME, 0);
then the system will end up calling foo_mess with the following arguments:
bytes to be the address of a symbol)
you are expecting it to be 4 bytes of an ieee758 floating point number) (for the sake of simplicity the above example assumes a 32bit system)
if you then happen to dereference so-acquired symbol, it will hopefully go kaboong (hopefully insofar as it will go kaboong without launching the missilies first)
mfgasdr IOhannes
thanks IOhannes, that's very helpful. by changing the callback signature to:
euclid_new(t_symbol *s, int argc, t_atom *argv)
i was able to get it working with A_GIMME. but one thing still bothers me: this is the constructor for the class, and i really only want to support instantiation with a single (float) creation argument. why should i have to jump through the hoops of A_GIMME? shouldn't the following signature:
euclid_new(t_floatarg f)
registered with class_new(..., A_DEFFLOAT) be sufficient for this simple case? this worked for me on 0.43.4; why doesn't it work in 0.47?
thanks again,
_chris
On Mon, Feb 6, 2017 at 3:57 AM, IOhannes m zmoelnig zmoelnig@iem.at wrote:
On 2017-02-06 07:42, Chris Chronopoulos wrote:
Code is attached. Any suggestions? What's this typechecking/A_GIMME
Pd has special, "optimized" functions to check the types of arguments.
basically, if you have a callback function (and from a pure "C" perspective, the class-methods of an objects are callback functions), you must correctly call that function. that is: if the callback function expects a symbols and then a float ("void foo_mess(t_myobject*x, t_symbol*s, t_float f)"), you must actually call it with a symbol and then a float ("foo_mess(x, s2, 2.3)"), to ensure that the compiler will put the correct bits at the right positions.
now in Pd, an external can wish for "any" function signature of the callback it wishes for, but it is the task of Pd (core) to actually call that callback function correctly. since "any" signature is pretty broad and it is tedious to write special callback callers for all the possibilities, it offers a (slightly less convenient) generic solution for "non-standard" callbacks: pass a list of atoms (aka A_GIMME) messages with more that 5 atoms are "non-standard", and therefore you must use A_GIMME.
the function signature of an A_GIMME is: int argc, t_atom*argv you then can iterate over the <argc> atoms in the <argv> array, and use them whoever you please (mainly: check that each element is of correct type, and use atom_getfloat() resp atom_getsymbol() to acccess the values).
i suspect (without having looked at your code), that you were simply changing the type to A_GIMME when registering the callback (in the class_addmothod() function), but then didn't bother to change the function signature of the actual callback function.
so if your callback signature is: void foo_mess(t_myobject*x, t_symbol*s, t_float f); and you register it with class_addmethod(x, (t_method)foo_mess, gensym("foo"), A_GIMME, 0);
then the system will end up calling foo_mess with the following arguments:
- the first 4 bytes are the address to the object (good)
- the next 4 bytes are an int value (bad because you are expecting the 4
bytes to be the address of a symbol)
- the next 4 bytes being the address of some t_atom-array (bad, because
you are expecting it to be 4 bytes of an ieee758 floating point number) (for the sake of simplicity the above example assumes a 32bit system)
if you then happen to dereference so-acquired symbol, it will hopefully go kaboong (hopefully insofar as it will go kaboong without launching the missilies first)
mfgasdr IOhannes
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/ listinfo/pd-list
On 2017-02-12 22:23, Chris Chronopoulos wrote:
registered with class_new(..., A_DEFFLOAT) be sufficient for this simple case? this worked for me on 0.43.4; why doesn't it work in 0.47?
oh.
of course Pd can do *that* for you. the problem here is, that your code is just bogus: class_new() expects a variable number of arguments. but in C there is no way to determine the actual number of arguments passed to the function. So you need to somehow tell the called function when it should stop to expect more arguments.
this can either be done by putting that number into one of the
function-arguments. an example is the dsp_add()
function. e.g.:
dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
the second argument (4) tells the dsp_add() function that there are 4 more arguments to come.
alternatively (if the data permits it), you can use a special "terminating") element with a magic value (usually this is just NULL); the function would then read it's arguments one by one until in encounters that special argument, after which it would stop. this is the case with the class_new() function. e.g.:
clip_class = class_new(gensym("clip~"), (t_newmethod)clip_new, 0,
sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
the varidiac part is "A_DEFFLOAT, A_DEFFLOAT" and it *must* be 0-terminated. so class_new() knows that there are only two (2) more valid arguments.
it seems that with your call to class_new(), you were just "lucky" and the compiler you used back then with Pd-extended happened to insert a NULL value after your A_DEFFLOAT. so your statement "this worked for me on 0.43" should actually read: "this accidentally happened to work for *me* at some point".
but really you were relying on undefined behaviour, and should just fix the original code.
fgamsdr IOhannes
oh, i see. i thought the argument after A_DEFFLOAT was the default value of the float. so it's a 0-termination - got it!
On Mon, Feb 13, 2017 at 4:03 AM, IOhannes m zmoelnig zmoelnig@iem.at wrote:
On 2017-02-12 22:23, Chris Chronopoulos wrote:
registered with class_new(..., A_DEFFLOAT) be sufficient for this simple case? this worked for me on 0.43.4; why doesn't it work in 0.47?
oh.
of course Pd can do *that* for you. the problem here is, that your code is just bogus: class_new() expects a variable number of arguments. but in C there is no way to determine the actual number of arguments passed to the function. So you need to somehow tell the called function when it should stop to expect more arguments.
this can either be done by putting that number into one of the function-arguments. an example is the
dsp_add()
function. e.g.:dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
the second argument (4) tells the dsp_add() function that there are 4 more arguments to come.
alternatively (if the data permits it), you can use a special "terminating") element with a magic value (usually this is just NULL); the function would then read it's arguments one by one until in encounters that special argument, after which it would stop. this is the case with the class_new() function. e.g.:
clip_class = class_new(gensym("clip~"), (t_newmethod)clip_new, 0, sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0);
the varidiac part is "A_DEFFLOAT, A_DEFFLOAT" and it *must* be 0-terminated. so class_new() knows that there are only two (2) more valid arguments.
it seems that with your call to class_new(), you were just "lucky" and the compiler you used back then with Pd-extended happened to insert a NULL value after your A_DEFFLOAT. so your statement "this worked for me on 0.43" should actually read: "this accidentally happened to work for *me* at some point".
but really you were relying on undefined behaviour, and should just fix the original code.
fgamsdr IOhannes
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/ listinfo/pd-list