obex: how to get return values from object_method()

Olaf Matthes's icon

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

Joshua Kit Clayton's icon

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

Olaf Matthes's icon

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

Joshua Kit Clayton's icon

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