MATLAB Answers

1

How to pass a method as a function handle

Asked by Lucas Abbade on 7 Nov 2019
Latest activity Commented on by Guillaume
on 8 Nov 2019
Ok, so I have an object as a property of a class. This object has methods. I need to use an object's method as a handle, like follows:
classdef MyClass
properties
my_obj_with_methods
end
methods
function r = foo(obj, func, a, b)
r = func(a, b);
end
function r = bar(obj)
r = obj.foo(@obj.my_obj_with_methods.my_method, 1, 2);
end
function r = foobar(obj)
r = obj.foo(@obj.my_obj_with_methods.my_other_method, 1, 2);
end
end
end
When I try to do this, I get:
"Undefined function or variable 'obj.my_obj_with_methods.my_method"
1 - I know I can solve this with an `if/else`, but I thought "hey, Matlab is a functional language, I could use those great functional features!"
2 - I'm not complicating things, I have good reason to want to do this
Thanks!

  0 Comments

Sign in to comment.

Products


Release

R2018a

3 Answers

Answer by Richard Brown on 7 Nov 2019
 Accepted Answer

I think the issue is that to use the @ construction, the thing you're applying it to needs to be named function in scope. There is no function "called" obj.my_obj_with_methods.my_method, that construction accesses the method another way.
so you can use the following piece of delight which does work, but is as ugly as sin:
classdef MyClass
properties
my_obj_with_methods
end
methods
function r = foo(obj, func, a, b)
r = func(a, b);
end
function r = bar(obj)
r = obj.foo(@(varargin) obj.my_obj_with_methods.my_method(varargin{:}), 1, 2);
end
function r = foobar(obj)
r = obj.foo(@(varargin) obj.my_obj_with_methods.my_other_method(varargin{:}), 1, 2);
end
end
end
Or ... use python (**ducks**)

  1 Comment

It might be ugly but it worked as I intended, so that's good enough for me, thanks!

Sign in to comment.


Answer by Guillaume
on 7 Nov 2019

Can't tell you why it doesn't work as you have written, I suspect that this has to do with how the . operator is implemented in matlab. It may be worth raising a service request with Mathworks to get a full explanation.
It's easily worked around though. You have to remember that obj.method(arg1, arg2, ...) is the same as method(obj, arg1, arg2, ...), so:
classdef MyClass
properties
my_obj_with_methods
end
methods
function r = foo(obj, func, subobj, a, b) %method should be static since it doesn't use obj
r = func(subobj, a, b);
end
function r = bar(obj)
r = obj.foo(@my_method, obj.my_obj_with_methods, 1, 2);
end
function r = foobar(obj)
r = obj.foo(@my_other_method, obj.my_obj_with_methods, 1, 2);
end
end
end
Of course, in this simple example, a better solution would be:
classdef MyClass
properties
my_obj_with_methods
end
methods
function r = foo(obj, func, a, b) %method can no longer be static
r = func(obj.subobj, a, b);
end
function r = bar(obj)
r = obj.foo(@my_method, 1, 2);
end
function r = foobar(obj)
r = obj.foo(@my_other_method, 1, 2);
end
end
end

  1 Comment

Thanks for the explanation, unfortunately, I don't this will solve my problem because `my_obj_with_methods` is actually a Python object, I'll try it out anyway

Sign in to comment.


Answer by Steven Lord
on 7 Nov 2019

Are my_method and my_other_method Static or non-Static methods of the class of the value stored in the my_obj_with_methods property of your object? Let's take a simple little class with a constructor, one Static method, and one non-Static method.
classdef example489847
properties
x
end
methods
function obj = example489847(xin)
obj.x = xin;
end
function q = twoTimes(obj)
q = 2*obj.x;
end
end
methods(Static)
function whatAmI
disp('I am an example489847');
end
end
end
I can make a handle to twoTimes.
>> fh = @twoTimes;
This function handle fh has no knowledge of the example489847 class. If I call this function handle with an instance of this class as input, then MATLAB will determine that it should call the twoTimes method of the example489847 class. But if you were to call it with other data, like a normal double array, MATLAB doesn't know of a twoTimes function that accepts a double so it throws an error. If there was another class with a twoTimes method, I could call the function handle with an instance of that class and have its twoTimes method run.
>> obj = example489847(42);
>> y = fh(obj)
y =
84
>> fh(42)
Undefined function 'twoTimes' for input arguments of type 'double'.
To make a function handle to a Static function, you need its "full name".
>> fh2 = @example489847.whatAmI;
>> fh2()
I am an example489847
>>
This is just like how you would call it if you wanted it to execute immediately.
>> example489847.whatAmI()
I am an example489847
>>

  4 Comments

Show 1 older comment
Those are non-static methods, and I can't change them because `my_obj_with_methods` is from a Python lib. But thanks very much for your answer, it made things much clearer for me!
Guillaume, you are correct that for a non-Static method the object comes into play only when you call the function handle not when you define the function handle. Let's take a slightly different example.
classdef SL2
methods
function why(~) % Yes, I know, this should be Static
disp('Why not?')
end
end
end
There is a function named why in MATLAB proper that can accept a scalar double value. If I make a function handle:
fh = @why;
MATLAB doesn't know and doesn't care how fh may eventually get called at this point. Technically, it actually doesn't care whether the function to which it refers even exists yet, though I'm not sure how useful that would be off the top of my head.
fh2 = @thisFunctionDoesNotExistWhenICreatefh2; % Does not error
fh2(1) % This will error
% Create a function with 1 input named thisFunctionDoesNotExistWhenICreatefh2
% Do NOT rerun the first line of this example
fh2(1) % This will call the new function
I can call it with a double or with an instance of SL2 and it will work equally well.
>> fh(42)
To satisfy the tall and good not excessively terrified mathematician.
>> fh(SL2)
Why not?
There isn't a way to create fh as a handle to the why method of the SL2 class, so that it can only ever be called on an instance of the SL2 class.
Steven, it's actually a bit more murky than that. You can create function handle to method class using the syntax:
@obj.method
we do that frequently in App designer, for example when defining callbacks.
And that works also with my demo class above. Interestingly, something I hadn't noticed before, matlab does a bit of magic here:
>> mc = MethodClass; %class defined in previous comment
>> fh = @mc.foo; %function handle to class method
>> fh(1) %no problem calling the method
ans =
2
>> fh
fh =
function_handle with value:
@(varagin)mc.foo(varargin{:})
So, matlab has rewritten the function handle a bit!
However, add an extra level of indirection with an additional dot in the expression:
>> mystruct.mc = MethodClass;
>> fh = @mystruct.mc.foo;
>> fh(1)
Unrecognised function or variable 'mystruct.mc.foo'
>> fh %no more magic :(
fh =
function_handle with value:
@mystruct.mc.foo
Basically, we can't chain two subsref calls when creating a function handle but the parser will let it through when it's two dot calls and the error is caught at runtime, whereas with {} and () it's caught at parse time:
>> fh = @mystruct.mc.foo; %parser is fine. Runtime error when called
>> mycell{1} = mc;
>> fh = mycell{1}.foo; %parser error
Error: Invalid expression...
>> mc(2) = MethodClass;
>> fh = mc(1).foo; %parser error
Error: Invalid expression...

Sign in to comment.