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