cannot update class definition in Matlab - matlab

I am running into an infuriating problem with Matlab, and an earlier answer to apparently the same problem didn't help me, unfortunately. I apologize that the question is rather long - you need quite a bit of information to reproduce the problem (I tried to trim it as much as I could...)
The problem is this: No matter what I do, after I have used a class I cannot "make Matlab forget". Values used seem to be persistent, and edits to the class definition won't "stick". In the latter case, the error message is:
Warning: The class file for 'myClass' has been changed; but the change
cannot be applied because objects based on the old class file still
exist. If you use those objects, you might get unexpected results. You
can use the 'clear' command to remove those objects. See 'help clear'
for information on how to remove those objects.
I get that message even after
>> clear all
>> clear functions
>> clear ans
Somehow the class definition is persistent despite my attempts to clear it. To make matters worse, when I modify a value of an instance of the class, then clear it, the value is somehow not "forgotten". To illustrate, here is the source code of myClass:
% a simple class definition that shows the problem that I cannot
% figure out how to redefine a class without restarting Matlab
classdef myClass < handle
properties
precursors = {'none'};
numPre = {1};
value = 1;
end
methods
function obj = myClass(pre, num, val)
% constructor
if nargin > 0
obj.precursors = pre;
obj.numPre = num;
obj.value = val;
end
end
function v = sumVal(obj)
% find the sum of the value of all precursors
n = numel(obj.precursors);
v = 0;
for ii = 1:n
pc = obj.precursors{ii};
if isa(pc, 'myClass')
if ii==1
v = 0;
end
v = v + sumVal(pc) * obj.numPre{ii};
else
v = obj.value;
end
end
end
end
% only the following named instances may exist:
enumeration
grandpa ({'none'}, {1}, 1)
father ({myClass.grandpa}, {3}, -1)
son ({myClass.father}, {2}, -1)
end
end
In a fresh instance of Matlab, I do the following:
>> son = myClass.son;
>> sumVal(son)
ans =
6
>> grandpa = myClass.grandpa;
>> grandpa.value = 5;
>> sumVal(son)
ans =
30
So far, so good. The sumVal function discovers the fathers and grandfathers, and the sumVal is computed correctly (6 * 1 in the first case, 6 * 5 in the second case).
Now I delete "everything" (I think):
>> clear all
>> clear functions
>> clear ans
And I create just one variable:
>> son = myClass.son;
Now comes the kicker - the unexpected answer
>> sumVal(son)
ans =
30
When I inspect the variables loaded, I find
>> whos
Name Size Bytes Class Attributes
son 1x1 112 myClass
There is no grandpa instance, and the class definition file was not touched. Yet, the value of grandpa (which I created, then deleted) is somehow persistent.
And when I make a small change to the myClass.m file, and try to create a new variable (after a clear all), I get the message shown above. All of which leads me to my question:
Where is Matlab hiding an instance of my class so that variables are persistent after a clear all, and how do I clear the workspace (without restarting) so the class definition is "reset"?
I don't know if it matters, but I'm using Matlab 7.14.0.739 (R2012a)

You have an intermediate instance myClass.father that is not being destroyed by MATLAB. You have to deleteit yourself
>> clear grandpa
>> delete(son.precursors{1})
>> clear son
>> clear classes
>> son = myClass.son
son =
son
>> sumVal(son)
ans =
6
Edit:
Alternatively, you can add a destructor to your class
function delete(obj)
if isa(obj.precursors{1}, 'myClass')
delete(obj.precursors{1});
end
end
and use delete(son) instead of leaving it to clear function to destroy. You can extend this to your case and recursively delete all instances in your tree.

Those instances are "hiding" in the myClass enumeration class itself. Matlab is storing a reference to each of those named instances so when you reference them like myClass.father you get the same object back, instead of it constructing a new one. Probably similar to how values are stored in Constant properties on classes.
If you have any other classes that refer to the myClass.xxx enumerated instances in Constant properties, enumerations, or persistent variables, they could also be holding on to references to them.
Try doing clear classes a few times in a row instead of just once.
To help debug this, you could put a couple debugging printf() statements in the constructor and destructor for this class, so you can see when the instances are really created and cleaned up.
function obj = myClass(pre, num, val)
% constructor
if nargin > 0
obj.precursors = pre;
obj.numPre = num;
obj.value = val;
end
printf('myClass: created (%d, %d, nargin=%d)\n', obj.numPre, obj.value, nargin);
end
function delete(obj)
printf('myClass: deleting (%d, %d)\n', obj.numPre, obj.value);
end

Related

How to make a member public in an class object created using MATLAB Class folder?

I'm trying to reuse a package in which all classes are written using Class folder i.e all methods are files inside a folder #myclassname. I would like to read the members of the class object. In normal classdef classes, declaring a property public solves the problem. In my case, I didn't find any solutions and I don't want to rewrite the whole package.
Minimal example explaining my problem:
simple class: \MatlabPath\#mytestclass\mytestclass.m
%myconstructor
function res =mytestclass()
st.a=1;
res=class(st,mfilename);
end
Current behavior:
>> obj=mytestclass();
>> obj.a
Access to an object's fields is only permitted within its methods.
Desired:
>> obj=mytestclass();
>> obj.a
ans =
1
Thank you
I know you don't want to rewrite the full package but if property access is important you might prefer that to the actual solution.
In the first MATLAB OOP classes, organised as you indicated (all methods in one folder), the programmer had to code their own subsref and subsasgn methods if they wanted customized access to their class properties.
I must say I did play a bit with it but quickly gave up as construction of simple classes was extremely tedious. MATLAB probably picked up on that and in later release introduced the new Classdef model. Despite still having a few quicks (due to MATLAB passing almost everything by value), this Classdef model is infinitely more convenient than the old formula.
If you want to stick with the old model, I can give you a minimalist example which will give you the desired behaviour. If you are only interested in exposing a few properties you can just replicate that a few times. If you plan to have all the properties of your package exposed, then I guarantee you that rewriting the package will be less effort than applying my solution to an extensive existing code base.
So in your folder : \MatlabPath\#mytestclass\, you now need two files:
mytestclass.m (I added a couple of different properties for example)
function res =mytestclass()
st.scalar = 1 ;
st.array = [1 2 3] ;
st.cellarray = {'1' '2' '3'} ;
res=class(st,mfilename);
end
and your own subsref.m:
function out = subsref(obj,S)
thisS = S(1) ;
if strcmp(thisS.type , '.')
out = obj.(thisS.subs) ;
elseif strcmp(thisS.type , '{}')
% You might have to code this case too
elseif strcmp(thisS.type , '{}')
% You might have to code this case too
end
% recursive call if the struct S has more than one element
if numel(S) > 1
S(1) = [] ;
out = subsref(out,S) ;
end
end
Armed with that, you can now access your properties:
>> obj.scalar
ans =
1
>> obj.array
ans =
1 2 3
>> obj.cellarray
ans =
'1' '2' '3'
This minimal subsref works for indexing into the arrays as well:
>> obj.array(2:end)
ans =
2 3
>> obj.cellarray(2)
ans =
'2'
And it also works for assignment:
>> obj.scalar = 125 ;
>> obj.scalar
ans =
125
>> obj.array(2:end) = [8 9] ;
>> obj.array
ans =
1 8 9
Keep in mind that this is a minimal example, some property type might not respond well to these simple cases, in which case you will have to add specific code to handle each particular case.
Hoki's answer gives the right solution, and allows customizing which members can be accessed. You can even design the class so you can 'index' with names that don't match any members.
But if all you want is to expose all class members, you can simplify the subsref function to:
function out = subsref(obj,S)
out = builtin('subsref',obj,S);

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

How to make a Matlab structure constant once it is already created?

Suppose I have a function defined in foo.m. This function can take a parameter thing of type struct. Once foo makes changes to thing, I want to "lock" thing so that it can no longer be changed. I essentially want to make it constant. I want to do this to ensure it isn't modified further down the line. How do I do this in Matlab?
You should
define the variable in the function to be persistent
lock your function in the memory using mlock.
mlock locks the currently running function in memory so that subsequent clear functions do not remove it. Locking a function in memory also prevents any persistent variables defined in the file from getting reinitialized.
Solution 1: Good if you don't know what form your struct will have in advance
You could 'capture' that variable with an anonymous function handle and only refer to your structure with that from now on. An anonymous function handle captures the state of the workspace at the time it is created. You will be able to access its elements as if it were the original struct, but if you try to assign to it, you'll generate an error.
E.g.
>> S_.a = 1;
>> S_.b = 2;
>> S = #() S_;
>> S_.a = 3;
>> S_
S_ =
scalar structure containing the fields:
a = 3
b = 2
>> S()
ans =
scalar structure containing the fields:
a = 1
b = 2
It's almost identical in syntax, except for the annoyance that you'll have to call it with ().
I've used it on the terminal here, but obviously it can easily also be used in the context of a function.
Small caveat; if you redefine and overwrite the anonymous function, obviously, this backfires, since it will inherit whatever new workspace it had access to at the time of the redefinition.
Solution 2: Good if you know your struct's form in advance:
Assume you know in advance that your struct will only contain fields a and b. Create a class with the same properties restricting 'SetAccess', e.g.
classdef ConstStruct
properties (GetAccess = 'public', SetAccess = 'private')
a
b
end
methods
%constructor
function obj = ConstStruct(S)
obj.a = S.a;
obj.b = S.b;
end
end
end
Then in your main code:
>> MyStruct = struct('a',1,'b',2)
MyStruct =
a: 1
b: 2
>> MyStruct = ConstStruct(MyStruct)
MyStruct =
ConstStruct with properties:
a: 1
b: 2
>> MyStruct.a
ans =
1
>> MyStruct.a = 2
You cannot set the read-only property 'a' of 'ConstStruct'.

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!

When can I pass a function handle?

I have a function for cached evaluation. As one of the arguments, it takes a function handle. Under some circumstances, the function handle is unaccessible, and I don't quite understand why. The example below shows what got me stumped:
>> A.a = #plus; feval(#A.a, 1, 1)
ans =
2
>> clear A
>> A.a.a = #plus; feval(#A.a.a, 1, 1)
Error using feval
Undefined function 'A.a.a' for input arguments of type 'double'.
So, if I have a function handle stored as a structure member, I can pass it along fine if it's one level deep, but not if it's two levels deep. In my real use case, I have a structure D that holds many (117) instances of various classes, so I actually have stct.obj.meth, where stct is a structure, obj is a class instance/object, and meth is a method. Passing #stct.obj.meth fails, but if I assign A = stct.obj, then passing #A.meth succeeds.
Under what conditions can I pass a function handle as an argument, so that it's still accessible down the stack?
Edit: Although in the use case above, I could simply remove the # because #plus is already a function handle. However, consider the situation here:
>> type cltest.m
classdef cltest < handle
methods
function C = mymeth(self, a, b)
C = a + b;
end
end
end
>> A.a = cltest();
>> feval(#A.a.mymeth, 1, 1)
Error using feval
Undefined function 'A.a.mymeth' for input arguments of type 'double'.
>> b = A.a;
>> feval(#b.mymeth, 1, 1)
ans =
2
In this case, I need the # before A.a.mymeth...
Introducing classes was a big deal for MATLAB. So big, in fact, that they still do not work properly today. Your example shows that structure access and class method access conflict, because they had to overload the the meaning of dot '.' and didn't get it to work seamlessly. It all more or less works fine when you are calling class methods explicitly by their name on the MATLAB console, e.g. in your example >> A.a.mymeth(1,1). But when you have any type of indirection, it soon breaks.
You tried getting the function handle by >> #A.a.mymeth, which MATLAB cannot make sense of, probably because it gets confused by the mixed structure/class thing. Trying to work around using str2func doesn't work either. It works, again, only for explicit name access, as shown here. It breaks for your example, e.g. >> str2func('b.mymeth'). It does not even work inside the class. Try other indirections and watch them fail.
Additionally, MATLAB does not like giving you a class method's handles. There's no function for it. There's no way to get all function handles in one go, or even dynamically by a name string.
I see three options here. First, try changing your program, if possible. Do these functions need to sit in a classdef?
Second, follow your or nispio's workaround. They both create a temporary variable to hold a reference to the class instance in order to create a non-mixed access to its member methods. The problem is, they both require explicitly naming the function. You have to explicitly put this code for every function involved. No way to abstract that out.
Third, cheat by giving out your class' method handles from the inside. You can give them out in a structure.
classdef cltest < handle
methods
function C = mymeth(self, a, b)
C = a + b;
end
function hs = funhandles(self)
hs = struct('mymeth', #self.mymeth, ...
'mymeth2', #self.mymeth2);
end
end
end
You can then access the handles by name, even dynamically.
>> A.a = cltest;
>> feval(A.a.funhandles.mymeth, 1, 1);
>> feval(A.a.funhandles.('mymeth'), 1, 1)
ans =
2
But be careful, by using this you can access Access=private methods from outside.
Try this:
feval(#(varargin)A.a.mymeth(varargin{:}),1,1);
It is a little kludgy, but it should work.
EDIT:
The way it works is by creating an Anonymous Function that takes a variable number of arguments, and dumps those arguments into the method A.a.mymeth(). So you are not actually passing a pointer to the function A.a.mymeth, you are passing a pointer to a function that calls A.a.mymeth.
An alternative way of achieving the same thing without using varargin would be:
feval(#(x,y)A.a.mymeth(x,y),1,1);
This creates an anonymous function that accepts two arguments, and passes them along to A.a.mymeth.
<speculation> I think that it must be inherent in the way that the unary function handle operator # works. The Matlab parser probably looks at #token and decides whether token is a valid function. In the case of a.mymeth it is smart enough to decide that mymeth is a member of a, and then return the appropriate handle. However, when it sees A.a.mymeth it may discover that A is not a class, nor does A have a member named a.mymeth and therefore no valid function is found. This seems to be supported by the fact that this works:
A.a.a = #plus; feval(A.a.a,1,1)
and this doesn't:
A.a.a = #plus; feval(#A.a.a,1,1)
</speculation>
You can get around it by introducing a separate function that corrects what # operator is not doing:
function h=g(f)
x = functions(f);
if ~strcmp(x.type, 'anonymous')
h = evalin('caller', ['#(varargin)' x.function '(varargin{:})']);
else
h = f;
end
end
Now for your example:
>> feval(g(#A.a.mymeth), 1, 1)
ans =
2
>> feval(g(#b.mymeth), 1, 1)
ans =
2
I think this will have the smallest impact on your code. You can make it a bit more elegant but less robust and/or readable. The uplus method is not defined for function_handle class so you can create uplus.m in folder #function_handle somewhere in your path with this content:
function h=uplus(f)
x = functions(f);
if ~strcmp(x.type, 'anonymous')
h = evalin('caller', ['#(varargin)' x.function '(varargin{:})']);
else
h = f;
end
end
Now you just need to use +# instead of #. For your examples:
>> feval(+#A.a.mymeth, 1, 1)
ans =
2
>> feval(+#b.mymeth, 1, 1)
ans =
2