MATLAB - create reference (handle?) to variable
Asked Answered
G

3

10

Suppose I have the following class:

classdef myClass < handle
    properties
        A = 1
    end
    methods
        function obj = myClass(val)
            obj.A = val;
        end
    end
end

Say I instantiate an instance of this class, then manipulate it slightly and then copy it. As it's a handle class, the "copy" is really just another instance of the same object:

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> disp(w.A)
   15

But I would like to watch A without needing to instantiate myClass. Naively doing

>> value = w.A

doesn't work, since this just copies the value; changning w.A later on will not change value.

Is there a way to provide a "pointer" or "reference" to w.A without having to create a separate handle class? I'd rather keep the notation w.A rather than something like w.A.value (with me having to create the handle class to contain that value).

EDIT: I am using this functionality in order to help encapsulate my code for use with my research lab. I am designing an interface between MATLAB and Arduino to control air and ground vehicles; I was hoping to access stuff like "vehicle.pwmMax", "vehicle.flightCeiling", etc, to encapsulate the underlying object: "vehicle.Globals.pwmMax.value", etc.

Gonfanon answered 16/8, 2011 at 21:59 Comment(3)
I think the answer is no; I believe you need to keep a reference to the class instance around and do foo.A every time you want to refer to instance variable A.Zanezaneski
@strictlyrude27: if you can explain how you intend to use this, there might a better way to do it...Agrostology
updated original post to explain my intentionGonfanon
D
16

You could do this by with a PropertyReference class

classdef PropertyReference < handle
    %PropertyReference Reference to a property in another object    
    properties
        sourceHandle
        sourceFieldName
    end

    properties (Dependent = true)
         Value
    end

    methods                
        function obj = PropertyReference (source, fieldName)            
            obj.sourceHandle = source;
            obj.sourceFieldName = fieldName
        end
        function value = get.Value( obj )
            value = obj.sourceHandle.(obj.sourceFieldName);
        end

        function set.Value( obj, value )
            obj.sourceHandle.(obj.sourceFieldName) = value;
        end
        function disp( obj )
            disp(obj.Value);
        end
    end              
end

Continuing your example, you could then use PropertyReference as follows:

q = myClass(10);
>> q.A = 15;
>> ref = PropertyReference(q,'A');
>> disp(ref)
   15
>> q.A = 42;
>> disp(ref)
   42

Usage of the PropertyReference class is a bit awkward but the original class remains unchanged.

EDIT - Added disp function overload as per strictlyrude27 comment

Darwen answered 17/8, 2011 at 0:1 Comment(3)
Looks like a really nice implementation. The only thing I (personally) don't quite like about it, is the string argument 'A', but I guess that's a matter of preference (and MATLAB has limited support for auto-completing fields anyhow and there is no way around that string argument using such an implementation).Pal
I like this, this could do quite nicely. Furthermore I can overload disp() to display ref.value immediately.Gonfanon
@RAAC - Was the reference to an object derived from handle? Haven't tried 2015a but still works in 2014a.Darwen
P
5

I don't think there is anything that will do exactly as you want, given all your constraints.

However, I'm not really clear on your notational issues. Why do you want to retain the notation w.A while you are considered about value not changing? Keeping the notation w.A similar is not a real issue.

Using some modified code, I can produce following execution:

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A
    15
>> value = w.Aref;
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20

But there is no way around the notation value() as that is the turning point of the implementation; which I think is the closest you can get to what you want. You get the behavior above when you use the following code to implement myClass:

classdef myClass < handle
properties
    A = 1;
end
methods
    function obj = myClass(val)
        obj.A = val;
    end
    function a = Aref(obj)
        a =  @()(obj.A);
    end
end
end

So you see that the Aref method actually returns a function handle which fetches the value from the object. This also means that this reference is read-only!

Also note that you will have to instantiate a myClass instance before you are able to get the value of A (where would you get the value of A from otherwise?). This instance does not have to be visible inside your current workspace (e.g. another function scope), since the myClass instance is stored within the function handle value.

Drawback of this method is that you only get a read-only reference, you will have to use the call value() to get the actual value instead of the function handle (so that changes the notation, but not the one you wanted to keep (or at least it can be made so by substituting A in my code by Aval and renaming Aref to A). Another drawback is that resolving value might be a bit slower than simply resolving a variable (whether that's a problem will depend on your usage of value()).

If you want some of the notations changed, this can be done by using dependent properties:

classdef myClass < handle
    properties (Access=private)
        Aval = 1;
    end
    properties (Dependent)
        A;
    end
    methods
        function obj = myClass(val)
            obj.A = val;
        end
        function a = get.A(obj)
            a =  @()(obj.Aval);
        end
        function set.A(obj,value)
            obj.Aval = value;
        end
    end
end

The equivalent execution of above is given by:

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
    15
>> value = w.A;
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20

edit: I thought of another way to implement this, which is simpler (i.e. just keep the class of your original post) but it requires you to change the code in other places. The basic idea behind it is the same as the first ones, but without encapsulating it in the object itself (which makes the object cleaner, IMHO).

>> q = myClass(10);
>> q.A = 15;
>> w = q;
>> w.A()
    15
>> value = @()(w.A);
>> value()
    15
>> w.A = 20;
>> value()
ans =
    20
Pal answered 17/8, 2011 at 0:3 Comment(0)
A
2

Since you are working with a handle class, both q and w in your example refer to the same object in memory; they are themselves a "pointer"/"reference" to the object they represent.

So continuing your example, if you make changes to one, it will be reflected in the other.

>> q = myClass(10);
>> w = q;
>> q.A = 99;
>> disp(w.A)
    99

Also note that you are not creating another instance of the class when you call w = q;. Compare the following examples in terms of memory space:

>> q = myClass(rand(7000));
>> m = memory; disp(m.MemUsedMATLAB)
   792870912
>> w = q;
>> m = memory; disp(m.MemUsedMATLAB)
   792834048

Against:

>> q = myClass(rand(7000));
>> w = myClass(rand(7000));
??? Error using ==> rand
Out of memory. Type HELP MEMORY for your options.

EDIT

Playing around with this, I came up with the following hackish solution.

First we create a wrapper function around the class constructor. It creates an object as usual, plus it returns a function handle that acts as a read-only accessor to a closure variable synced with the original object property using a "PostSet" events listener.

The only change to the original class is to add the SetObservable property attribute:

myClass.m

classdef myClass < handle
    properties (SetObservable)
        A
    end
    methods
        function obj = myClass(val)
            obj.A = val;
        end
    end
end

myClassWrapper.m

function [w A] = myClassWrapper(varargin)
    w = myClass(varargin{:});
    A = @getWA;

    %# closure variable
    a = w.A;

    %# add listener to when w.A changes
    addlistener(w, 'A', 'PostSet',@changeCallback);

    function val = getWA()
        %# return the value of the closure variable
        val = a;
    end
    function changeCallback(obj,ev)
        %# update the closure variable
        a = ev.AffectedObject.A;
        %#fprintf('Value Changed to %g\n',a)
    end
end

Now we can use the wrapper as:

>> [w a] = myClassWrapper(10);
>> a()
ans =
    10
>> w.A = 99;
>> a()
ans =
    99
Agrostology answered 16/8, 2011 at 22:25 Comment(2)
Right, and that was my point - I want to reference a specific property, q.A, without having to keep the other stuff in q. So an assignment like a = q.A, but if q.A changes, a changes as well. Since q.A is a value and not a handle, a won't change if q.A does.Gonfanon
@strictlyrude27: AFAIK you can't have a simple (double) variable track the value of an object property, but I could be wrong..Agrostology

© 2022 - 2024 — McMap. All rights reserved.