I'm having a weird problem using object oriented matlab, and I'm not sure if I'm completely missing something, or I've hit some unusual behaviour.
I have a superclass defined as follows:
classdef (Abstract) AbstractSimulationData < matlab.mixin.Heterogeneous
properties (Abstract)
name
data
end
properties
timeStamp = -1
end
methods (Abstract)
CalculateData(obj, t)
end
methods
function data = GetData(obj, t)
if obj.timeStamp == t.t
data = obj.data;
else
obj.timeStamp = t.t;
obj.CalculateData(t);
data = obj.data;
end
end
end
end
When the concrete class implements the method CalculateData I intend it to set the superclass property data.
Two concrete classes from this are:
classdef A < AbstractSimulationData
properties
name = 'A'
data = []
end
methods
function obj = A
% No special initialisation
end
function CalculateData(obj, t)
test = t.B.GetData(t)
obj.data = rand(2,10);
end
end
end
And:
classdef B < AbstractSimulationData
properties
name = 'B'
data = containers.Map
end
methods
function obj = B
% No special initialisation
end
function CalculateData(obj, t)
if isempty(obj.data)
obj.data('left') = 1;
obj.data('right') = 2;
end
% Other operations
end
end
end
Within the A.CalculateData method I call the GetData method for B. This in turn calls the B.CalculateData method.
Now, as it filters back up, the data property gets set correctly so that B.GetData returns the value that was set in B.CalculateData.
But, when I go the next step up to A.GetData it returns [], the initialised value. Going through debug mode, the calculations definitely work in A.CalculateData and it seems to set data correctly, but when it jumps back up to the superclass obj.data is the default value from A.
A minimum working example to get this behaviour:
t.B = B();
t.t = 1;
a = A;
a.GetData(t);
>> test =
Map with properties:
Count: 2
KeyType: char
ValueType: any
>> ans =
[]
This is all part of a pretty big project, so it's hard to give the full context, but if it's possible from this, can someone explain why setting superclass data works for B but not A?
If it makes a difference B is returning a container.Map and A is returning 2xn matrix.
As far as I can tell from the documentation, it should work.
Related
Short-short version
Is it possible to get an handle to a subclass of Copyable rather than getting a copy?
Context
I have a class hierarchy that i want to have Copyable.
classdef A < matlab.mixins.Copyable
properties
stuff;
end
methods
function action(self)
% ...
end
end
end
Various subclasses provide variations on the main behavior, and a particular subclass works to aggregate several of those behviors for sequenctial application
classdef multiA < classA
properties
nElements = 1;
elements;
iElement = 0;
currentElement;
end
methods
function self = multiA()
end
function addElement(self, fhdl)
if self.nElements == 0
self.nElements = 1;
self.elements = {fhdl}; % Store handles to constructors
else
self.nElements = self.nElements + 1;
self.elements(self.nElements) = fhdl; % Store handles to constructors
end
end
function nextElement(self)
if self.iElement < self.nElements
self.iElement = self.iElement + 1;
fhdl = self.elements(self.iElement);
self.element = fhdl(); % instantiate with constructors
end
end
function action(self)
if self.iElement > 0 && self.iElement <= self.nElements
self.currentElement.action(); % Pass use request to constructed objects
end
end
end
end
The user is expected to build up objects with addElement, and work through the list with nextElement invoking the behavior of the contained objects as needed with `action.
multi = multiA();
multi.addElement( #OneSubClass );
multi.addElement( #AnotherSubClass );
multi.addElement( #ThirdSubClass );
% ...
multi.nextElement();
multi.action();
The problem emerges
The multiA class passes some basic unit tests and I started trying to apply it. And it seemed to be working. Then I get a situation where multi.action() seems to be flipping unpredictably back and forth between two behaviors.
It seems unlikely, but just conceivable that somehow the object of type OneSubClass is sticking around after I have moved on to AnotherSubClass.
The (wrong?) solution
Write a unit test which captures the handle of each element as it is instantiate and checks that this remains the handle in use until the next one is instantiated. Something like:
multiA.nextElement();
ehdl = multiA.currentElement;
% do things
testCase.veriftySameHandle(multiA.currentElement,ehdl);
Alas ehdl seems to be a copy of the stored element rather than a handle to it even though I have not explicitly invoked copy(). (I say that because ehdl retains the state of the contained object at the time of nextElement() while the fresh access of currentElement shows the expected changes.)
Is there a way to force the retention of a handle to an object derived from matlab.mixins.Copyable?
I'd like to save a particular class property to disk, while ignoring the rest of the class properties. I think MATLAB allows the saveobj method to be overridden for this purpose. However, this saves the class object with only that property attached. I want to save the property itself, without any of the class information.
I might think that a suitable method would look like:
classdef myClass
properties
myProp
end
methods
def b = saveobj(a)
b = a.myProp;
end
def Save(a,fname)
save(fname,'a.myProp');
end
end
end
But neither of these have the desired effect. Can anyone help me, please?
You can overload the save function itself without having to go through saveobj:
classdef myClass
properties
myProp
end
methods
function [] = save(a,fname,varargin)
myProp = a.myProp; %#ok<PROP,NASGU>
save(fname,'myProp',varargin{:});
end
end
end
Then on the command window:
>> foo = myClass();
>> foo.myProp = 4;
>> foo.save('var.txt');
>> bar = load('var.txt','-mat');
>> bar.myProp
ans =
4
The first method (the one involving saveobj) is actually correct. For the purposes of discussion let us consider this simple class:
classdef testclass
properties
x
end
methods
function this = testclass(x)
this.x = x ;
end
function a = saveobj(this)
a = this.x ;
end
end
end
When you ask MATLAB to save an instance of your class, it will use the saveobj method when you call save if one exists. The output of this method can be an object, a struct, an array, whatever. You want to test that this has occurred, and you do something natural like this:
>> obj = testclass('hi')
obj =
testclass with properties:
x: 'hi'
>> save tmp.mat obj
>> clear all
>> load tmp.mat
>> obj
obj =
testclass with properties:
x: []
>>
And this is where I suspect your confusion arises. You expect obj to be a char but instead it's an empty object of class testclass. (You can verify that it is just an instance of the object based on the saved definition of the class, and that it is not created by calling the empty constructor.)
This may seem rather confusing until you understand how loadobj works. In order for MATLAB to know which static method to call on load, it saves the class definition in conjunction with whatever output you are providing from your custom saveobj method. When you call load it then loads the class definition and calls the loadobj static method if one exists. We can test this by modifying the class definition above:
classdef testclass
properties
x
end
methods
function this = testclass(x)
this.x = x ;
end
function a = saveobj(this)
a = this.x ;
end
end
methods( Static )
function this = loadobj(a)
this = testclass(a) ;
end
end
end
If you set a breakpoint in the loadobj method you can verify that the type of a is indeed char as you expect.
In Matlab, I can define a class as such:
classdef klass < handle
properties(Dependent)
prop
end
end
Matlab is perfectly happy instantiating an object of this class, even without defining a getter for prop. It only fails when I try to access it (understandably). I'd like to set the GetMethod dynamically based upon the property's name.
Unfortunately, even when the property is Dependent, the meta.property field for GetMethod is still read-only. And while inheriting from dynamicprops could allow adding a property and programmatically setting its GetMethod in every instance, I don't believe it could be used to change an existing property. I may have to go this route, but as prop must exist for every object I'd prefer to simply set the getter on a class-by-class basis. Is such a thing possible?
An alternative solution could be through some sort of catch-all method. In other languages, this could be accomplished through a Ruby-like method_missing or a PHP-like __get(). But as far as I know there's no (documented or otherwise) analog in Matlab.
(My use case: this class gets inherited by many user-defined subclasses, and all their dependent properties are accessed in a similar way, only changing based on the property name. Instead of asking users to write get.* methods wrapping a call to the common code for each and every one of their dependent properties, I'd like to set them all dynamically with anonymous function pointers containing the necessary metadata).
Here is my proposal: create a method in the superclass called add_dyn_prop. This method is to be called in the subclasses instead of creating a dependent property the usual way.
The idea is that the superclass inherit from dynamicprops and use addprop to add a new property, and set its accessor methods manually based on its name.
classdef klass < dynamicprops
methods (Access = protected)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
% NOTE: this has to be a simple function_handle (#fun), not
% an anonymous function (#()..) to avoid infinite recursion
p.GetMethod = #get_method;
p.SetMethod = #set_method;
% nested getter/setter functions with closure
function set_method(obj, val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
now in the subclass, instead of defining a dependent property the usual way, we use this new inherited function in the constructor to define a dynamic property:
classdef subklass < klass
%properties (Dependent, SetAccess = private)
% name
%end
%methods
% function val = get.name(obj)
% val = 'Amro';
% end
%end
methods
function obj = subklass()
% call superclass constructor
obj = obj#klass();
% define new properties
add_dyn_prop(obj, 'name', 'Amro');
add_dyn_prop(obj, 'age', [], false)
end
end
end
The output:
>> o = subklass
o =
subklass with properties:
age: []
name: 'Amro'
>> o.age = 10
o =
subklass with properties:
age: 10
name: 'Amro'
>> o.name = 'xxx'
You cannot set the read-only property 'name' of subklass.
Of course now you can customize the getter method based on the property name as you initially intended.
EDIT:
Based on the comments, please find below a slight variation of the same technique discussed above.
The idea is to require the subclass to create a property (defined as abstract in the superclass) containing the names of the desired dynamic properties to be created. The constructor of the superclass would then create the specified dynamic properties, setting their accessor methods to generic functions (which could customize their behavior based on the property name as you requested). I am reusing the same add_dyn_prop function I mentioned before.
In the subclass, we are simply required to implement the inherited abstract dynamic_props property, initialized with a list of names (or {} if you dont want to create any dynamic property). For example we write:
classdef subklass < klass
properties (Access = protected)
dynamic_props = {'name', 'age'}
end
methods
function obj = subklass()
obj = obj#klass();
end
end
end
The superclass is similar to what we had before before, only now is it its responsibility to call the add_dyn_prop in its constructor for each of the property names:
classdef klass < dynamicprops % ConstructOnLoad
properties (Abstract, Access = protected)
dynamic_props
end
methods
function obj = klass()
assert(iscellstr(obj.dynamic_props), ...
'"dynamic_props" must be a cell array of strings.');
for i=1:numel(obj.dynamic_props)
obj.add_dyn_prop(obj.dynamic_props{i}, [], false);
end
end
end
methods (Access = private)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
%p.Transient = true;
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
p.GetMethod = #get_method;
p.SetMethod = #set_method;
% nested getter/setter functions with closure
function set_method(obj,val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
Note: I did not use ConstructOnLoad class attribute or Transient property attribute, as I am still not sure how they would affect loading the object from a saved MAT-file in regards to dynamic properties.
>> o = subklass
o =
subklass with properties:
age: []
name: []
>> o.name = 'Amro'; o.age = 99
o =
subklass with properties:
age: 99
name: 'Amro'
Check if this is what you want. The problem is that the user will need to get the properties using (), which may be quite boring, but anyway, I think this way you can change the variables. You can't change them directly on the class, but you can change the objects property values on demand. It doesn't need to change the values on the constructor, you can do that using another function that will be inherited by the classes.
klass1.m
classdef(InferiorClasses = {?klass2}) klass < handle
methods
function self = klass
selfMeta = metaclass(self);
names = {selfMeta.PropertyList.Name};
for name = names
switch name{1}
case 'prop_child_1'
self.(name{1}) = #newGetChild1PropFcn;
case 'prop_child_2'
self.(name{1}) = #newGetChild2PropFcn;
end
end
end
end
methods(Static)
function out = prop
out = #defaultGetPropFcn;
end
end
end
function out = defaultGetPropFcn
out = 'defaultGetPropFcn';
end
function out = newGetChild1PropFcn
out = 'newGetChild1PropFcn';
end
function out = newGetChild2PropFcn
out = 'newGetChild2PropFcn';
end
klass2.m
classdef klass2 < klass
properties
prop_child_1 = #defaultGetChildPropFcn1
prop_child_2 = #defaultGetChildPropFcn2
end
methods
function self = klass2
self = self#klass;
end
end
end
function out = defaultGetChildPropFcn1
out = 'defaultGetChildPropFcn1';
end
function out = defaultGetChildPropFcn2
out = 'defaultGetChildPropFcn2';
end
Output:
a = klass2
b=a.prop_child_1()
b =
newGetChild1PropFcn
In Matlab, I would like a data structure that looks like so:
DataStruct
.model
.Q
.Qchol
.
.
.system
.
.
The structure may well be a class, although I don't really need all the other functionality that goes with oop.
But I require
If Q is assigned something, then automatically Qchol = cholcov(Q).
If Qchol is assigned something, then automatically Q = Qchol' * Qchol.
Meanwhile, both Q and Qchol are stored for fast read-access
And Q and Qchol are writable through simple assignment, e.g.: DS1.mod.Q = value
I know I can make model a class, and have set/get methods for Q and Qchol. However, this really seems like an overkill for just two matrices (plus maybe some more fields). Also Matlab warns me that I should not access other properties during in a set method.
So: What is the best way to have such data structures, preferably without warnings?
You basically want assignment (DS1.mod.Q = value) to have side-effects, which inevitably implies a setter, and hence a class. You should either drop this requirement, or write a class.
If you wish to avoid definition of properties in the class declaration, you could use Dynamic Properties, which allows you to add properties at runtime (although with some telltale syntax addprop()).
EDIT
Patric, the problem goes deeper then just M-lint. Consider the following class:
classdef cantInstantiateMe < handle
properties
x
minus_x
end
methods
function obj = cantInstantiateMe(x)
obj.x = x; % <-- this calls set.x(), which calls set.minus_x(), which calls set.x(), ...
obj.minus_x = -x;
end
function set.x(obj, value)
obj.x = value;
obj.minus_x = -value; % <-- this gives an M-Lint warning
end
function set.minus_x(obj, value)
obj.minus_x = value;
obj.x = -value;
end
end
end
This class cannot be instantiated, because each setter calls the other setter (this is not Matlab-specific). Trying to instantiate on my machine gives:
??? Maximum recursion limit of 500 reached. Use set(0,'RecursionLimit',N)
to change the limit. Be aware that exceeding your available stack space can
crash MATLAB and/or your computer.
At this point I think you have two options:
Make either Q or Qchol a dependent property. This will come at the cost of re-calculating the dependent property each time you read-access it.
Use some private shadow properties e.g. shadow_Q and shadow_Qchol which will be set when the setter for the public property is called, and returned when their getter is called. Similar to:
function set.x(obj, value)
obj.shadow_x = value;
obj.shadow_minus_x = -value;
end
function value = get.x(obj)
value = obj.shadow_x;
end
Note the I did not test this properly, so I don't know all implications in Matlab. In other languages I'm familiar with, this should work fine.
Regarding the warning - my approach is that it is safe to disable the warning, as long as you really know what you are doing.
As suggested by #bavaza, one way to implement this is to use a dependent property with corresponding shadow private properties.
Below is the code implementing the inner data structure (inspired by this post). You need to use composition to make an instance of this class a property of the outer object:
classdef Model < handle
properties (Dependent)
Q
Qchol
end
properties (Access = private)
Q_
Qchol_
end
methods
function obj = Model()
end
function val = get.Q(obj)
val = obj.Q_;
end
function val = get.Qchol(obj)
val = obj.Qchol_;
end
function set.Q(obj, val)
obj.Q_ = val;
obj.Qchol_ = cholcov(val);
end
function set.Qchol(obj, val)
obj.Qchol_ = val;
obj.Q_ = val'*val;
end
end
end
Setting one value using the exposed dependent properties affects both underlying variables:
>> m = Model
m =
Model with properties:
Q: []
Qchol: []
>> m.Qchol = rand(3)
m =
Model with properties:
Q: [3x3 double]
Qchol: [3x3 double]
I am creating a class in MATLAB and while I have little experience with objects, I am almost positive I should be able to set a class property using a class method. Is this possible in MATLAB?
classdef foo
properties
changeMe
end
methods
function go()
(THIS OBJECT).changeMe = 1;
end
end
end
f = foo;
f.go;
t.changeMe;
ans = 1
Yes, this is possible. Note that if you create a value object, the method has to return the object in order to change a property (since value objects are passed by value). If you create a handle object (classdef foo<handle), the object is passed by reference.
classdef foo
properties
changeMe = 0;
end
methods
function self = go(self)
self.changeMe = 1;
end
end
end
As mentioned above, the call of a setting method on a value object returns the changed object. If you want to change an object, you have to copy the output back to the object.
f = foo;
f.changeMe
ans =
0
f = f.go;
f.changeMe
ans =
1