obex: how to get return values from object_method()


    Jun 29 2006 | 8:31 pm
    I hope this is a stupid question (because I already stared at all this code for too long): how do I access the return value of a object_method() call in an obex external?
    The pattr SDK docs state: "If the receiver object can respond to the message, object_method returns the result. Otherwise, the function will return 0."
    The function I'm calling is supposed to return a double, the code I got to compile look slike this:
    double *retval = NULL;
    retval = (double *)object_method(x->x_childobj, gensym("doit"), array); if (*retval != NULL) post("do something with it");
    But as soon as I try to access the retval (even checking whether it's == NULL) Max crashes. (Windows XP, latest Max, latest SDK, VC++ 6.0.)
    I can't use object_method_typed() here because I have to pass my function a float** array that won't fit into a t_atom. On a similar function call (that also crashed) the use of object_method_typed() solved the problem of getting the return value.
    And a related question: would it hurt if I call the function directly (without using the object_method... call)? At least it doesn't crash this way. - I really don't like the idea of putting my float** into a hughe amount of t_atoms...
    Olaf

    • Jun 30 2006 | 9:01 am
      On Jun 29, 2006, at 1:31 PM, Olaf Matthes wrote:
      > I hope this is a stupid question (because I already stared at all > this code for too long): how do I access the return value of a > object_method() call in an obex external? > > The pattr SDK docs state: "If the receiver object can respond to > the message, object_method returns the result. Otherwise, the > function will return 0." > > The function I'm calling is supposed to return a double, the code I > got to compile look slike this: > > double *retval = NULL; > > retval = (double *)object_method(x->x_childobj, gensym("doit"), > array); > if (*retval != NULL) > post("do something with it"); > > But as soon as I try to access the retval (even checking whether > it's == NULL) Max crashes. (Windows XP, latest Max, latest SDK, VC+ > + 6.0.)
      You can't use a pointer to grab a double as above (unless you actually return a double pointer, which will remain valid beyond the extent of the function called). So, you'll need to create a double return value prototype like the following:
      typedef double (* t_double_method)(void *, ...);
      Then get the method pointer with zgetfn or class_getmethod()
      t_double_method myfn;
      myfn = zgetfn(x,gensym("doit"));
      and then call the method appropriately:
      double retval; retval = myfn(x->x_childobj, array);
      Sorry, if there are typos, as I'm writing this quickly in an airport, but this should give you an idea.
      -Joshua
    • Jul 01 2006 | 8:47 am
      Joshua Kit Clayton wrote:
      > You can't use a pointer to grab a double as above (unless you actually > return a double pointer, which will remain valid beyond the extent of > the function called). So, you'll need to create a double return value > prototype like the following: > > typedef double (* t_double_method)(void *, ...); > > Then get the method pointer with zgetfn or class_getmethod() > > t_double_method myfn; > > myfn = zgetfn(x,gensym("doit")); > > and then call the method appropriately: > > double retval; > retval = myfn(x->x_childobj, array); > > > Sorry, if there are typos, as I'm writing this quickly in an airport, > but this should give you an idea.
      Hi Joshua,
      thanks for the explanation... I tried it and it still crashes... but will try also on Mac and with a different compiler on Windows to make sure it's not another compiler issue. As long as I call the functions directly everything is working fine. But of course, that's not the goal when using obex style objects.
      I also noticed that using object_method_typed() returns me an empty return value. When I think about it, how does Max know whether my function returns A_FLOAT or A_LONG, for example? Do I have to register it somewhere indicating the type of return value (smilar to class_addmethod() where I tell Max what arguments my function takes in)?
      Olaf
    • Jul 01 2006 | 3:23 pm
      On Jul 1, 2006, at 1:47 AM, Olaf Matthes wrote:
      > > thanks for the explanation... I tried it and it still crashes... > but will try also on Mac and with a different compiler on Windows > to make sure it's not another compiler issue. > As long as I call the functions directly everything is working > fine. But of course, that's not the goal when using obex style > objects.
      Would be good to see a more complete code example. AFAICT, your previous example was crashing because you were dereferencing a double *pointer* rather than setting a double *value*, and a double *value* can't be returned using object_method()
      Another strategy would be to prototype the method to take a double pointer which is set rather than returning the double as the function's return value. This can be called with object_method (but *not* object_method_typed which is restricted to A_LONG, A_FLOAT, A_SYM, A_GIMME, and A_GIMMEBACK typed prototypes). For example.
      void myobject_doit(t_myobject *x, double *array, double *rv) { if (array&&rv) { // do something wih the array // then set the "output" argument *rv = thevalue; } }
      // the above should be prototyped A_CANT (untyped) and called as follows
      double rv=0; object_method(x->x_childobj, gensym("doit"), array, &rv);
      > I also noticed that using object_method_typed() returns me an empty > return value. When I think about it, how does Max know whether my > function returns A_FLOAT or A_LONG, for example? Do I have to > register it somewhere indicating the type of return value (smilar > to class_addmethod() where I tell Max what arguments my function > takes in)?
      object_method_typed() requires a typed prototype with the signature A_GIMMEBACK for a return value (this is a new introduction for methods exposed to JS/Java, and not terribly documented). Otherwise, the return value argument is NULL. A_GIMMEBACK is limied to the atom type, though an atom can be an object pointer in this case. You could in theory create your own object class which wraps a double precision value, but that seems like more work.
      Here's a simple example of an A_GIMMEBACK method.
      t_max_err obex_tester_gimmeback(t_obex_tester *x, t_symbol *s, long ac, t_atom *av, t_atom *rv) { post("obex tester gimmeback av = %d",atom_getlong(av)); if (ac && av) { atom_setlong(rv,atom_getlong(av)*atom_getlong(av)); } else { atom_setlong(rv,-1); } return MAX_ERR_NONE; }
      -Joshua