1

You can call methods of an object using function handles. Example:

classdef foo
  methods
    function value=foofun(this)
      value=1;
    end
  end
end

and then

fun=@foofun;
fun(foo);

The handle is just a string I guess, and it doesn't bother Matlab that it can't tell which function is intended before the handle is used. The same handle could even be used to call different methods if different type objects are used as the first parameter.

To capture the outputs of the function you can do this:

outputs=cell(numberOfOutputs,1);
[outputs{:}]=fun(foo);

However, if the function handle was a parameter in the code, then the number of outputs is a problem. For handles to normal functions nargout would solve that, but now nargout can't help because the handle doesn't determine on it's own which function will be called. nargout would need a second input, the type of the object that is going to be used.

It's a bit inelegant to need both the handle and the number of outputs as parameters. Is there another way to call methods indirectly and still capture all the outputs?

Use-case

Imagine a graph with nodes and edges between nodes. Each edge has a few data objects associated with that particular edge. I'm interested in doing various calculations on those data objects.

Examples

  • Take all the data objects associated with a specific edge and calculate spectra of those objects.
  • Use the prettiest data object from each edge and get the spectra from those objects.
  • Choose ten edges randomly, choose randomly one data object from each of those and then compare the objects with a specific, separate, object.

Design

The idea is to separate the selection of the objects and the calculations into different functions. This way the user can combine any calculation with any selection method. Each time a new method is added to the data objects, it can already be called with the existing object selection methods and each time a new selection method is added, it can be used with all the available calculation methods.

A simplified version of a function I'm currently using looks like this

function [ outs ] = callForEach(objects, fun, numberOfOutputs, extraArguments)
    for ii=1:length(objects)
      out=cell(1,numberOfOutputs);
      [out{:}]=fun(objects{ii},extraArguments{ii,:});
      outs(ii,:)=out;
    end
end

I'd like to be able avoid the parameter numberOfOutputs. It's an inconvenience for the user and it's more error prone to do it like this.

I guess I could just force a same interface for every method: everybody returns a cell array if they need several outputs. I can then assign that cell array, or whatever it is that is returned, into a single cell. However all the built-in functions seem to prefer to use multiple outputs instead, so there's at least a stylistic difference. If I ever wanted to use one of the built-ins as the handle, I'd need to write a wrapper to make the built-in conform to the interface.

Memto
  • 15
  • 5
  • What's the intended use-case for this? Typically, an indirect call via a function handle is needed when a library function requires one and specifies the intended interface which the handle must match (e.g., `ode45`'s `odefun` and optional `Events` handles). It would be easier to just pass around the class instance and operate directly on that. – TroyHaskin May 12 '17 at 00:52
  • The edit answers most of my questions, except "what is the intended interface for the handles?" I see that `@prettiest` should take an array of `edges` and return an array of the the prettiest; and that `@spectrum` should operate on an array of `dataObjects` and return an array of the associated sepctra. Where are the multiple outputs? – TroyHaskin May 17 '17 at 01:45
  • I was actually thinking that @spectrum only operates on a single object, so that whoever calls it can decide whether to parallelize the calls or not. However, this difference doesn't matter. – Memto May 17 '17 at 15:50
  • I still don't know where the unknown number of outputs comes into play. Also, is this set of method you're planning to give users access to bound to these objects in play? If so, you can dynamically call them based on a string like `this.(nameOfMethod)(x);` and possibly replace the need for handles with a user-defined string for the method. – TroyHaskin May 18 '17 at 04:29
  • I'm sorry, I don't understand what's missing. :D The set of methods is naturally bound to the objects in play, they are methods(members of the class), not functions. `this.(nameOfMethod)(x)` is an alternative, I guess, but I don't think it makes a difference. The signatures of the methods are different, so I won't know how many returns I should try to capture. – Memto May 21 '17 at 08:49
  • And that's the use I'm still struggling to find. The leading example shows use of `[outputs{:}]`, but I haven't seen where this is applied or needed in the actual use-case you gave. Where's the need for variadic output, an example of use within the methods, and an example of expected input/output pair? – TroyHaskin May 21 '17 at 19:20
  • I tried to improve the question. There's no need for variadic output, just the regular constant number of outputs for each function. – Memto May 23 '17 at 18:02

1 Answers1

0

Check this class as foo.m. By running foo.main() you recover most of the addressed questions.

This is not the maximum elegance in OOP, but you have Matlab as is.

Remember the obj variable is MANDATORY, in and out. No obj, no dot fanciness.

The final calls, HAVE to include the f1. Putting a ~ lost not only the fanciness, but the object and its data.

Besides the obj treatment, nargin|nargout|varargin|varargout are valid inside a function body only, and is runtime dependent, as usually. One cannot in general know how much parameters you will throw, because is equivalent to the length of the varargout cell.

classdef foo
    properties
        value;
    end
    methods
        function [obj,flag]=fun1(obj,this)
            obj.value=this+1;
            flag=1;
        end
        function [obj,flag]=fun2(obj,this)
            obj.value=2*this;
            flag=2;
        end
        function [obj,varargout]=fun3(obj,varargin)
            for i=1:nargin-1
                varargout{i}=varargin{i};
            end
            obj.value=0;
        end
    end
    methods (Static)
        function main()
            if 0
                %% Run this line
                foo.main()
            end
            f1=foo();
            [f1,flag]=f1.fun1(10);
            flag
            f1.value
            f=@fun2;
            %[f1,flag]=f1.f(10); % fails
            flag
            f1.value
            [f1,flag]=f1.('fun2')(10);
            flag
            f1.value
            [~,flag]=f1.fun2(10); % fails
            flag
            f1.value
            [f1,a1,a2,a3,a4]=f1.fun3('x1',2,[],@(x)(x>1))
        end
    end
end
Brethlosze
  • 1,533
  • 1
  • 22
  • 41