Attribute weirdness

    Dec 10 2010 | 9:48 pm
    II'm experiencing some weird attribute behaviour with one of my externals.
    In the new routine I call:
    attr_args_process (x, argc, argv);
    The local variable then appears to be set correctly up until the end of the new routine.
    At the next functon call to the object (that I am aware of) the local variable is no longer correct.
    There is NOWHERE in the code where I set the local variable other than the top of the new routine (before the call to attr_args_process).
    Any advice?

    • Dec 10 2010 | 9:58 pm
      Forgot to say. The externals are loaded in an abstraction with the attribute set using @chan #1.....
      It works when I manually create the object... arrrrghhh...
    • Dec 10 2010 | 11:46 pm
      Done some testing on a cut down version. In certain cases the attributes seemed to be parsed/set by MaxMSP *ontop* of the call to attr_args_process. The custom setters I have are being called twice.....
      Is this a bug, or could it be caused by incorrect implementation. I'm pretty sure the same code works without issue in Max 4....
    • Dec 11 2010 | 10:49 am
      Send some source code over. I'm sure it's something one of us can quickly spot.
    • Dec 11 2010 | 10:55 am
      Hi Jeremy - following debug I've found it is a problem to do with the attribute being saved and a replaceable argument.
      I'm not sure if this is *correct* behaviour but any advice you have is appreciated - source code shouldn't be necessary but if it's not clear from the below I'll post it:
      I have an abstraction that loads the external and tries to set one of the attributes using a replacable argument. Right now in a test version this looks like this:
      [pctest~ 6144 4096 @offset 2048 @length 6144 @chan #1]
      When I reload the abstraction with an argument I get the correct value in the new routine, but then I believe the saved value gets reloaded afterwards (I'm deferring low in the new routine to check the value just after). That's why I'm seeing a second call to the other attributes. In the case of attributes that are set directly in the abstraction they are simply reloaded as the same value which is fine, but the replaceable argument has not (obviously) been saved with the abstraction....
      Does this correspond to *correct* behaviour (I can see how it might). Is there a workaround other than making the attribute non-saveable?
    • Dec 11 2010 | 5:45 pm
      Go ahead and send the source to me privately (jeremy (at), if you don't want to post it. But I would like to see the source before I get too far into checking this out.
    • Dec 11 2010 | 5:54 pm
      I have a cut down example. I'm not too precious about the original source, but it's 1000 lines long - most of which is irrelevant.
      Even this could be smaller.... As far as I can see the new routine is called *then* saved attributes are reloading, so replaceable arguments have been saved in the abstraction as whatever a 0 would produce.....
      The source works in max 4 because you can't save attributes and that line was not being compiled.
      Hope this all makes sense...
      void *this_class;
      typedef struct _pctest
          t_pxobject x_obj;
      	void *obex;
      	// Attributes
      	long fft_size;
      	long chan;
      	long offset;
      	long length;
      } t_pctest;
      void pctest_free(t_pctest *x);
      void *pctest_new(t_symbol *s, short argc, t_atom *argv);
      t_max_err pctest_length_set(t_pctest *x, t_object *attr, long argc, t_atom *argv);
      t_max_err pctest_fftsize_set(t_pctest *x, t_object *attr, long argc, t_atom *argv);
      void pctest_dsp(t_pctest *x, t_signal **sp, short *count);
      int main(void)
      	post ("YES");
      	this_class = class_new("pctest~",
      	class_addmethod(this_class, (method)pctest_dsp, "dsp", A_CANT, 0);
      	class_addmethod(this_class, (method)object_obex_quickref, "quickref", A_CANT, 0);
      	// Add attributes
      	t_object *attr;
      	class_obexoffset_set(this_class, calcoffset(t_pctest, obex));
      	attr = attr_offset_new("fftsize", gensym("long"), 0, (method)0L,(method)pctest_fftsize_set, calcoffset(t_pctest, fft_size));
      	class_addattr(this_class, attr);
      	CLASS_ATTR_SAVE(this_class, "fftsize", 0);
      	attr = attr_offset_new("length", gensym("long"), 0,(method)0L,(method)pctest_length_set, calcoffset(t_pctest, length));
      	class_addattr(this_class, attr);
      	CLASS_ATTR_SAVE(this_class, "length", 0);
      	attr = attr_offset_new("offset", gensym("long"), 0,(method)0L,(method)0L, calcoffset(t_pctest, offset));
      	attr_addfilter_clip(attr, 0, 0, 1, 0);
      	class_addattr(this_class, attr);
      	CLASS_ATTR_SAVE(this_class, "offset", 0);
      	attr = attr_offset_new("chan", gensym("long"), 0, (method)0L,(method)0L, calcoffset(t_pctest, chan));
      	attr_addfilter_clip(attr, 1, 4, 1, 1);
      	class_addattr(this_class, attr);
      	CLASS_ATTR_SAVE(this_class, "chan", 0);
      	// Add dsp and register 
      	class_register(CLASS_BOX, this_class);
      	return 0;
      void pctest_free(t_pctest *x)
      void testattr (t_pctest *x, t_symbol *s, short argc, t_atom *argv)
      	post ("check chan %ld", x->chan);
      void *pctest_new(t_symbol *s, short argc, t_atom *argv)
      	// Setup the object and make inlets / outlets
      	t_pctest *x = (t_pctest *)object_alloc(this_class);
          dsp_setup((t_pxobject *)x, 1);
          outlet_new((t_object *)x,"signal");
      	// Set attributes from arguments
      	attr_args_process (x, argc, argv);
      	post ("the chan is %ld %ld", x->chan, x);
      	defer_low(x, testattr, 0, 0, 0);
      	return (x);
      t_max_err pctest_length_set(t_pctest *x, t_object *attr, long argc, t_atom *argv)
      	post ("Length set %ld %ld", x, atom_getlong(argv));
      	return MAX_ERR_NONE;
      t_max_err pctest_fftsize_set(t_pctest *x, t_object *attr, long argc, t_atom *argv)
      	post ("FFT Called %ld %ld", x, atom_getlong(argv));
      	return MAX_ERR_NONE;
      void pctest_dsp(t_pctest *x, t_signal **sp, short *count)
    • Dec 11 2010 | 6:08 pm
      Saved attributes are definitely restored after typed-in values (which makes sense if you consider that Max doesn't have a pointer to your object until you've returned from the new() function). This is one reason why CLASS_ATTR_SAVE() is recommended for use with UI objects, and not for normal newobj objects -- manual attribute freezing is better in that case.
      Your object code is 100% ok, as far as I can tell. If you get rid of the attr saving, you'll be fine.
    • Dec 11 2010 | 6:56 pm
      Thanks Jeremy - yes that confirms everything I suspected. Everything else works fine - I just wasn't aware that CLASS_ATTR_SAVE() is not recommended for non-UI objects (or really sure of the workings of freezing attributes).
      Now I can see that removing it makes everything work and the same possibilities are still available to the user.
      Thanks again,