Defining a set/get interface without using matlab hgsetget abstract class - matlab

I generally find the set/get interface defined in hgsetget very useful for setting and getting multiple parameters at the same time. I recently found it was especially suited for object construction.
Example:
classdef testclass < hgsetget
properties
A
B
C
D
end
methods
function obj = testclass(varargin)
if ~isempty(varargin)
set(obj,varargin{:})
end
end
end
end
Usage:
>> a = testclass('A',1,'B',2)
a =
testclass handle
Properties:
A: 1
B: 2
C: []
D: []
Despite the slowness of this interface I'm really happy of the flexibility it provides.
What is more annoying for my application is that I obtain a handle class (by inheritance from hgsetget).
To circumvent this my first guess was to construct an abstract class with my set/get definition inside. Very simply written it gives:
classdef (Abstract) myAbstractClass
methods
function obj = set(obj,varargin)
for i = 1:2:length(varargin)
obj.(varargin{i}) = varargin{i+1};
end
end
function val = get(obj,varargin)
val = cell(length(varargin),1);
for i = 1:length(varargin)
val{i} = obj.(varargin{i});
end
end
end
end
and then set it as superclass for my test class
classdef testclass < myAbstractClass
properties
A
B
C
D
end
methods
function obj = testclass(varargin)
if ~isempty(varargin)
set(obj,varargin{:})
end
end
end
end
However I must misunderstand something in the construction mechanism because here is what happens:
>> a = testclass('A',1,'B',2)
ans =
testclass
Properties:
A: 1
B: 2
C: []
D: []
Methods, Superclasses
a =
testclass
Properties:
A: []
B: []
C: []
D: []
Methods, Superclasses
If somebody knows the reason of this behavior, I'm totally open to her/his explanations
Thank you in advance
JM

Overall your design looks good, but within your constructor method testclass, you need to write
obj = set(obj,varargin{:});
Because you are using a value class rather than a handle class, you need to retrieve the output of your set method, otherwise you'll have set the property of something, and then discarded it and returned a completely different (and empty) obj as output.
Also, you need to add a semi-colon after the call to set; otherwise it will display the intermediate something.
You may also want to revise your opinion of handle classes; they're really very natural to use (just my opinion, but more natural in an OO context than value-behaviour).

Related

Problem with implementing abstract property in Matlab

In Matlab (2017b) I'm trying to implement a superclass with a yet undefined abstract property.
classdef Class_Test1
properties (Abstract=true)
obj
end
end
Subclasses should then implement this property and restrict it to a certain class, e.g.:
classdef Class_Test2 < Class_Test1
properties
obj#double = 123;
end
end
a = Class_Test2; now throws an error:
The property 'obj' restriction defined in class 'Class_Test2' must match
the property definition in base class 'Class_Test1'.
Is there no way to specify the class / type of obj in the implementation of the abstract class or am I missing something?
In the end I would like obj to be abstract in Class_Test1 to implement several subclasses, which will hold objects of different classes in obj.
Any help appreciated ...
MATLAB classes are weird... or in any case very different from what you expect from a class if you've learnt about classes in any other programming language.
One aspect of MATLAB is that it's an interpreted language. Stuff is evaluated when executed, not when compiled. So code doesn't always need to make sense.
So, we can solve your issue like this:
classdef (Abstract) foo
methods
function c = getclass(in)
c = class(in.obj);
end
end
end
The function foo.getclass seems to not make sense, since foo doesn't have a property obj. But because foo is abstract, we're never going to have an object of class foo on which we'll call the getclass method. Whenever getclass is called, it'll be a derived class. So, if we want to use this getclass method, we need to make sure that the derived class has a property obj:
classdef foo_uint8 < foo
properties
obj#uint8 = uint8(1);
end
end
It is now possible to call foo_uint8.getclass:
>> a = foo_uint8;
>> a.getclass
ans =
'uint8'
OK, so after looking into this further it seems that this is a deliberate limitation based on the design of Matlab. I'm not too familiar with OOP in other languages but would be interested to learn if this is an general OOP fundamental or rather Matlab specific.
As a workaround I have come up with the following solution:
classdef (Abstract) Class_Test1 < handle
properties
Obj
end
properties (Abstract = true, Constant)
ObjClass#char
end
methods
function set.Obj(obj_in,in)
if isa(in, obj.ObjClass)
obj_in.Obj = in;
end
end
end
end
... and the implementation ...
classdef Class_Test2 < Class_Test1
properties (Constant)
ObjClass#char = 'double'; % Define the allowed class here ...
end
end
I would still be interested to learn about other possible solutions.

Property initialization in MATLAB

In MATLAB there are two ways to initialize its properties. Either directly in the properties:
classdef A
properties
foo = 'bar';
end
end
or by explicitly defining a constructor:
classdef B
properties
foo;
end
methods this = B()
this.foo = 'bar';
end
end
Which one is the more preferable way?
(I am asking because there is a similar case in C++ where the preferred way is to initialize the member variables with a colon after the constructor rather than assigning variables within the constructor).
In most cases, the two methods behave the same, and the choice is just a preference depending on which you find clearer/easier to maintain etc.
But they are not the same, and there are cases where you will get yourself in trouble if you aren't aware of the differences.
The essential difference is that properties that are initialised in the constructor are initialised separately each time an object is constructed, whereas properties that are initialised in the properties block with a default value are initialised once, when the class definition is first read. The default value in a properties block is the default value of the class, not of the object; and (using reflection) you can query that default value even if no instance of the class has yet been instantiated.
In most cases, this makes no difference - but when the initial value is a handle object, or the output of a non-determinate function it does. So consider the following two classes:
classdef A
properties
foo
end
methods
function obj = A
obj.foo = containers.Map;
end
end
end
classdef B
properties
foo = containers.Map
end
end
Note that containers.Map is a handle class.
In class A, every time you create a new instance of A you get a new/different containers.Map for its foo. In B, every instance of B gets the same containers.Map for its foo, as the property is initialised only once, the first time the class definition is read. So if you modify foo for any object of class B, that change is propagated to all other instances of class B, as you can see:
>> a1 = A; a2 = A; a1.foo('greeting') = 'hello'; a2.foo('greeting') = 'bonjour';
>> a1.foo('greeting'), a2.foo('greeting')
ans =
'hello'
ans =
'bonjour'
>> b1 = B; b2 = B; b1.foo('greeting') = 'hello'; b2.foo('greeting') = 'bonjour';
>> b1.foo('greeting'), b2.foo('greeting')
ans =
'bonjour'
ans =
'bonjour'
>> % Note that b1.foo has changed as a result of setting b2.foo
This point about handle classes as default values often trips people up; but the behaviour is not specific to handle classes. For example, consider the following variation:
classdef A
properties
foo
end
methods
function obj = A
obj.foo = datetime('now');
end
end
end
classdef B
properties
foo = datetime('now')
end
end
Here A will store the creation time of each object, whereas B will store the time at which the class was first initialised, for all objects, no matter when they were created.
In case you find this behaviour confusing, see https://undocumentedmatlab.com/blog/handle-object-as-default-class-property-value, and particularly the comment thread underneath that article, for a discussion of the issue, and an explanation for the reasons MATLAB is designed in this way.
Edit: Great follow up question in the comments, regarding the behaviour of clear and its relation to this issue.
Using the second implementation of the classes above (with datetime), look at the following:
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:30'
ans =
'01-Sep-2018 18:59:30'
>> clear variables
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:48'
ans =
'01-Sep-2018 18:59:30'
>> clear classes
>> a = A; b = B; datestr(a.foo), datestr(b.foo)
ans =
'01-Sep-2018 18:59:57'
ans =
'01-Sep-2018 18:59:57'
So we first of all create an A and a B, and display their foos, and they both show the same time. Then we wait a little while, do clear variables, and we do it again. Note that the foo from A is the new time, and the foo from B is still the same as before. Finally we wait a little more time, we do clear classes, and we do it again. This time both A and B have the new time.
Why? Because clear variables merely removes references to the variables from the workspace. The class definition of B is not cleared, so when we create another B, it still uses the value from when the class definition was first read. clear classes, by contrast, also removes the class definition, so when we later construct a new B, it gets that time, as the class definition is then reread. All this is irrelevant to A, as foo is just given a value at construction time.
Note that clear classes clears all class definitions: you can clear the definition of only class B using clear B.
It is on your choice except for some cases.
Let me mention when we use the colon in Constructor function inC++:
1- Calling base class constructors
2- Initializing member variables before the body of the constructor executes.
No.#1 can be also used in the body but we should use the colon in No.#2 when the member is const.
BUT
But here is Matlab, with its own language syntax and structure.
Use which one do your job.
But here in Matlab when you want to define Constant properties you MUST define it in properties part and you can't do it in initializer/constructor function.
classdef NamedConst
properties (Constant)
R = pi/180; % Can't define it in constructor body, because it is `Constant properties`
end
%class body
end

MATLAB: save a class property

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.

Is it possible in one line to get a class value from a function and modify it?

If I have a class
classdef foo
properties
a = 0;
end
methods
function obj foo(obj)
obj.a = 5;
end
end
end
And a function
function result = GetFoo()
result = foo();
end
At the command interpreter:
>> x = GetFoo()
x =
foo
Properties:
a: 5
Methods
>> x.a = 10
x =
foo
Properties:
a: 10
Methods
this yields an instance of foo and the value 'a' can be assigned. However, doing the same in one step:
>> GetFoo().a = 10
GetFoo =
a: [1x1 struct]
This creates a new structure called GetFoo that overrides the class and gives it a member called 'a'. Rather than the code getting the result of GetFoo() (the class instance) and settings its property 'a' = 10, it does this instead. I can see that it is likely a hangover from the ability to create weakly typed structures on the fly and the code having to be backward compatible It also seems to be related to the fact that MATLAB has no concept of a pointer so every input/output argument is a deep copy and that the above even if it would work would be setting the value of a copy of the value GetFoo() is using to source its return object, then throwing it away.
Nonetheless the goal really is to be able to do all the work I need without creating and requiring to clear temporary variables. The intent is code maintainability as much as cosmetic style.
You're mixing together quite a few different issues/complaints in your question, but I suspect what you're looking for is a handle class. Inherit your class from handle (i.e. write classdef foo < handle, and your class will have what is basically pass-by-reference behavior. You're right that MATLAB doesn't use pointers, but with handle classes it does have references.
Separately, it's not possible in MATLAB to index in to the output of a function (i.e. to write GetFoo.a or GetFoo().a - you do need a temporary variable for that, whether or not it returns a value or handle class.
Note that in your example above, the result of the GetFoo().a = 10 is not an instance of foo with the property a equal to 10 - it is a structure with a field a equal to 10. Type class(GetFoo), and you'll see it's a struct, not a foo.
This is really ugly, but is just to raise the glove that was thrown about doing it in one line. :-)
First of all, I'll modify a bit the class so you'll see that the constructor is called:
classdef foo
properties
a = 0;
end;
methods
function obj = foo()
obj.a = 5;
display('foo!');
end;
end;
end
And the one-liner, ran in Command Window:
>> subsasgn(GetFoo(), struct('type', '.', 'subs', 'a'), 10);
foo!

Side effects of calling a MATLAB class instance don't persist

If I make the following toy class in MATLAB:
classdef testIt
properties
a
b
c
end
methods
function obj = testIt
obj.a = 1;
obj.b = 2;
end
function obj = set.a(obj,a)
obj.a = a;
end
function obj = set.b(obj,b)
obj.b = b;
end
function obj = addup(obj)
obj.c = obj.a + obj.b;
end
end
end
and then instantiate and call the addup method:
>> aTest = testIt
Properties:
a: 1
b: 2
c: []
>> aTest.addup
Properties:
a: 1
b: 2
c: 3
>> aTest
Properties:
a: 1
b: 2
c: []
The property c has not been created. Instead, I need to use the syntax:
>> aTest = aTest.addup
>> aTest
Properties:
a: 1
b: 2
c: 3
Can anyone explain to me why this is necessary?
Matlab supports two types of classes: handle classes, and value classes.
Value classes operate similar to structures and other Matlab variables, in that they are passed by value. Thus, if you want to modify an instance of a value class within a function, you need to return and overwrite the instance in the calling workspace.
Handle classes, on the other hand, are passed by reference. If you change the value of a property anywhere, the class is updated in all workspaces. For example, you can have a reference to the object in the base workspace, and one inside a GUI, and if you modify one, the other changes its value accordingly.
If you change your code to classdef testIt < handle, the objects will behave exactly the way you expect.
Also: Have a look at the documentation
add to the class definition:
classdef testIt < handle