1

I have a class that encapsulates access to an array in a wierd way; The class constructor takes a function handle which is some kind of transformation of indexes before passing them to the array

classdef MyClass
properties
    arr
    accessHandle
end
methods
function obj = MyClass(array, trans)
    obj.arr = array;
    obj.accessHandle = @(i) obj.arr(trans(i))
end
end

The problem is the anonymous function copies the array into it's own workspace and if we change the array, it doesn't change in the function. Essentially what is needed is to pass to the anonymous function the 'this' pointer/reference like in Java/C++/etc

The simple solution is to create a handle to the array and pass it along to the function:

classdef MyClass
properties
    arr
    accessHandle
end
methods
function obj = MyClass(array, trans)
    obj.arr = array;
    tmp = PropertyReference(obj, 'arr'); %See http://stackoverflow.com/questions/7085588/matlab-create-reference-handle-to-variable 
    %for the definition
    obj.accessHandle = @(i) tmp(trans(i));
end
end
end

The problem now is when I pass an instance of the class to a function, the reference passed still refers to the object outside the function:

function foo(ins)
    ins.arr = [1 2];
    disp(ins.accessHandle(1));
end
cl = MyClass([0 3], @(x) x);
foo(cl) //output 0 instead of 1
disp(ins.accessHandle(1)) //output 0

EDIT: The class should be a value class, the semantics are that when a copy of the class is made, the accessHandle field changes the array handle it uses.

How can I achieve the right semantics ?

Omer Rosler
  • 237
  • 1
  • 10
  • 2
    Would subclassing `handle` fix this? – TroyHaskin Dec 03 '16 at 15:34
  • @TroyHaskin @Suever I can't make it a handle like I explained in the last sentance; `foo` does not change the input, only it's copy – Omer Rosler Dec 03 '16 at 15:46
  • The equivalent C++ definition of `accessHandle` would be `[this](int x){return this->arr(trans(x));}` if that helps – Omer Rosler Dec 03 '16 at 15:49
  • You're not making `arr` a handle; you're making `MyClass` a `handle` class object which has pointer semantics. @Suever's answer appears to have your desired behavior. Please clarify with a more illustrative example if it is lacking. – TroyHaskin Dec 03 '16 at 15:53
  • @TroyHaskin See the edit and the new last line of the code – Omer Rosler Dec 03 '16 at 16:07
  • Keep in mind that unless the class is modified within the calling function, MATLAB will not produce a copy inline with its lazy-copying methodology. – TroyHaskin Dec 03 '16 at 16:15
  • @TroyHaskin I know that, but I do need to modify the copy inside the function, that's the problem – Omer Rosler Dec 03 '16 at 16:19

1 Answers1

2

Currently, your class is a value class (MATLAB's default). If you want to be able to pass objects around by reference (changes will be reflected in the original object), you'll to make it a handle class by subclassing handle

classdef MyClass < handle
    properties
        arr
        accessHandle
    end

    methods
        function obj = MyClass(array, trans)
            obj.arr = array;
            obj.accessHandle = @(i) obj.arr(trans(i))
        end
    end
end

And then you can use it the way you expect

m = MyClass([1 2 3], @(x)x);

m.accessHandle(2)
%   2

m.arr(2) = 5;

m.accessHandle(2)
%   5

More information about the difference between the two can be found here.

Edit

If you need MyClass to be a value class, then you could store the trans value as a property and then have a normal method to access the value

classdef MyClass
    properties
        arr
        trans
    end

    methods
        function obj = MyClass(array, trans)
            obj.arr = array;
            obj.trans = trans;
        end

        function res = access(obj, k)
            res = obj.arr(obj.trans(k));
        end
    end
end

Or if you want, you could make accessHandle a dependent property that returns a function handle.

classdef MyClass
    properties
        arr
        trans
    end

    properties (Dependent)
        accessHandle
    end

    methods
        function obj = MyClass(array, trans)
            obj.arr = array;
            obj.trans = trans;
        end

        function res = get.accessHandle(obj)
            res = @(i)obj.arr(obj.trans(i));
        end
    end
end
Suever
  • 64,497
  • 14
  • 82
  • 101
  • See the edit, the class should be a value class, the change is only for the definition of `accessHandle` – Omer Rosler Dec 03 '16 at 16:09
  • @OmerRosler Why does it need to be a value class? You can't do that without having a handle class. – Suever Dec 03 '16 at 16:10
  • @OmerRosler Also why can't you just pass the object into the `accessHandle` anonymous function? – Suever Dec 03 '16 at 16:12
  • 1
    @OmerRosler Or just make `accessHandle` a real method – Suever Dec 03 '16 at 16:13
  • Well, the class is an array with wierd way to access data, it has the semantics of a value class; the access function is what requires a handle to the class, not the class itself – Omer Rosler Dec 03 '16 at 16:16
  • Because it makes a copy of the object in the constructor, and if I change the array later on, it won't change at all inside `accessHandle` – Omer Rosler Dec 03 '16 at 16:17
  • @OmerRosler I've provided two alternatives if you need a value class – Suever Dec 03 '16 at 16:22
  • Great! Iused a variant of your example: I saved an `impl_access` as a variable which is defined as `@(arr)@(x) arr(trans(x))`, and the get method of `accessHandle` returns `impl_access(obj.arr)` – Omer Rosler Dec 03 '16 at 17:20