Given an object with custom get-methods for some properties, does Matlab execute some of the code (the getter) before the class constructor is executed?
Even if i set the default of a property to empty, and have a getter (!) open an io connection to a file, when I step through the debugger, even on the first line the object is already defined as file.io (with a filepath that corresponds the information available to the object before the constructor ran). How can this be, and whats the reasoning behind this implementation?
Edit: A breakpoint in the get method does not halt the debugger, so I'm not sure wether it is actually executed or not.
Edit 2: It seems like the getter is executed after the constructor is entered, after the debugger halts in the first line, before the first line is executed. No halt at breakpoint within get method though...
As per request, some code:
classdef Cat < handle
properties
filename
poop = []; % my data matrix the cat is there to produce/manage
end
methods
function obj = Cat(config)
obj.filename = config.FILENAME; % Halt debugger in this line
end
function value = get.poop(obj)
obj.poop = matfile(obj.filename)
value = obj.poop.ingredients; % 'ingeredients' being the name of the variable in poopfile.mat
end
end
end
To debug, I call
myCat = Cat(config)
from a different script. Workspace is cleared and path is rehashed.
When the debugger halts, obj.poop is not [], but is already a reference to some undefined file, and the reference to the linked file obj.poop.Source is empty, which is obvious, as obj.filename has not been set yet.
Test setup:
With a slightly modified class Cat.m:
classdef Cat < handle
properties
filename
poop = [];
end
methods
function obj = Cat(config)
display('In constructor.');
obj.filename = config.FILENAME;
end
function value = get.poop(obj)
display('In poop getter.');
obj.poop = matfile(obj.filename);
value = obj.poop.ingredients;
end
end
end
to display the execution order of the class methods, and the test.m script:
ingredients = 1:100;
save('a', 'ingredients');
config.FILENAME = 'a.mat';
myCat = Cat(config)
I got the following result:
>>test
In constructor.
myCat =
In poop getter.
Cat handle
Properties:
filename: 'a.mat'
poop: [1x100 double]
Methods, Events, Superclasses
Please note that the first assignment in the getter method was ended with semicolon (while in the original code was not).
In conclusion:
The get.poop() method is called after the constructor, as expected. This was tested on MATLAB R2012a, but I strongly believe that this is not a matter of version.
The reason for which get.poop() method is called is because the assignment myCat = Cat(config) is not ended with a semicolon ;.
Rationale:
The default behavior for assignments not ended with semicolon is to display the result of assignment. Displaying an object means, among other things, displaying the values of public properties. To get the value of the public property poop, get.poop() is called; that explains the getter call. Once the statement is changed to myCat = Cat(config);, the getter is not called anymore, because the result of assignment is not displayed anymore.
Later note:
Please note also that every request for display of the object will call the getter. So, yes, the getter might be called while the constructor is still halted by the debugger, because you inspect the poop member.
Related
In this article (https://blogs.mathworks.com/loren/2012/07/16/who-what-why-but-not-this/) near the bottom Loren says that class properties can be the same as keywords. However, how is this possible? If you write a classdef script any attempt to use a keyword (including class keywords like "events") in the properties block gets a red syntax error. Was she mistaken? I'm asking because I really want a property name to be a keyword for a particular application.
Its possible by the use of dynamic properties, for example:
classdef test < dynamicprops
methods
function obj = test()
end
end
end
var = test();
var.addprop ( 'events' );
var.events = 123;
It can make code harder to maintain and its a bit overkill if you only want to name a single property the same as a keyword, in that instance why dont you do something like capitilizing the var name, or prepending it with something - so it still reads like what you want but it doesn't cause the name clash:
classdef test
properties
Events
myIf
% etc...
end
methods
function obj = test()
end
end
end
I want to load a property on demand but i cant get it working.
I have a class with a transient property foo. So the property is not stored when the object is saved. When I use a method that is calling the property 'foo', I want that the value of foo is loaded from a separate mat file and stored into the object as long as it is in workspace.
I tried something with get and set methods but cant get it working. Is this possible? Or do I always add a line of code that is loading the property? The following code does not do what I want but it gives an indication what i tried.
In addition, the code below keeps loading the foo.mat file when the property is used. I want to load foo.mat only one time and store it as a property and retrieve the data from there instead of loading. The reason for my question is that the foo property is rather large i.e. a class with many properties in itself. I only want to load it when it is needed and dont want to store it in foobar class itself.
classdef foobar
properties(Transient = true)
foo
end
methods
function value = get.foo(obj)
if isempty(obj.foo)
value = load('foo.mat');
disp('load foo.mat');
end
end
function obj = set.foo(obj,value)
obj.foo = value;
end
end
end
You have two major problems here:
In your get.foo method, once you load the value, you never update the value of foo in the object, so it remains empty.
Even if you tried to update foo in your get.foo method, it would still be empty in the original object because your foobar class is a value class. Methods that modify a value class object have to return the modified object as an output, because they are essentially modifying a copy of the object. A set method of a value class returns a modified object that is used to overwrite the original object, but get methods don't return modified objects (since they aren't generally expected to modify them). To get around that limitation, you'll need the reference-like behavior of a handle class (here's a related question you may want to take a look at for more background).
So, in order to get the behavior you want you'd have to implement foobar as a subclass of the handle class and update the foo field when you first load it:
classdef foobar < handle % Inherit from handle class
properties(Transient = true)
foo
end
methods
function value = get.foo(obj)
if isempty(obj.foo)
value = load('foo.mat');
disp('load foo.mat');
obj.foo = value; % Update foo
end
value = obj.foo; % Return current foo value
end
function set.foo(obj, value) % Return value is unnecessary for handle class
obj.foo = value;
end
end
end
This should now give you the behavior you want (i.e. foo is only loaded when it is first accessed).
Note: Any method that invokes get.foo will initialize foo. One method you may overlook, because it's created by default for a class, is the disp method. The default display for a class object will show the class name followed by a list of non-hidden public properties and their values. Note what happens when I create an object of class foobar from above with and without a semicolon:
>> f = foobar; % Display is suppressed
>> f = foobar
f =
load foo.mat % foo gets initialized...
foobar with properties:
foo: [1×1 struct] % ...because its value is displayed here
If you want to avoid this, you can overload the disp function for your foobar object so that displaying the object doesn't access (and thus initialize) foo. For example, you can add this method to the above foobar class:
function disp(obj)
disp(' foobar object');
end
Now you won't initialize foo when displaying the object:
>> f = foobar
f =
foobar object % foo not yet initialized
>> a = f.foo;
load foo.mat % foo initialized because we accessed it
I would like to access the class name of the concrete class that's invoking a static method implemented in an abstract superclass.
This is the code (part of) of the abstract superclasss:
classdef (Abstract) AbstractJobProcessor < handle
properties (Abstract, Constant)
VERSION_MAJOR;
VERSION_MINOR;
LAST_MODIFIED;
end
...
methods (Static)
function res = getVersionMajor;
res = AbstractJobProcessor.VERSION_MAJOR;
end
function res = getVersionMinor
res = AbstractJobProcessor.VERSION_MINOR;
end
function res = getVersionInfo
res = sprintf('**CLASSNAME**: v%d.%02d (last modified: %s)',...
AbstractJobProcessor.VERSION_MAJOR,...
AbstractJobProcessor.VERSION_MINOR,...
AbstractJobProcessor.LAST_MODIFIED);
end
end
...
Basically, I would like to access the classname of the concrete subclass and use it in the method getVersionInfo in place of the string **CLASSNAME**.
All the methods returning meta information about a class (that I have found in the documentation) require a reference to an instance of the class (like, for example, mc = metaclass(object)).
The below function will give you what you want - subclass name, that was used when invoking an (inherited) static superclass method. Just call it inside your superclass method like you would any normal function:
className = getStaticCallingClassName();
What it does handle:
Both the case when method was invoked programmatically (i.e. by a running script / function), as well as when it was invoked from the command window.
Arbitrarily nested package names (i.e. classes located inside directories prefixed with +).
What it does not handle:
Does not work if the static method is called in a non-static context, i.e. on an object instance. But you should not be using such syntax anyway. This would've been possible if we were able to use evalin with 'caller' workspace recursively, but it does not work this way.
A brief explanation behind the idea: second entry in the stack trace, produced by dbstack, would correspond to the superclass, which we can use to extract the static method name. The next steps depend on:
If the method is invoked programmatically, third stack entry would point us to a line in the the parent script/function which we need to read, e.g. using dbtype. All that's left to do is extract the subclass name using regexp based on the method name.
If the method is invoked from command window, we query the last command and use that as the input for our regular expression.
Note that even if stack has 3 entries or more, it doesn't mean that the method was invoked programmatically. For example, if we've stopped on a breakpoint somewhere and invoke the method from command window, stack trace would be long, but regexp based on the line from the third stack trace entry will not give us the answer. In this case we fall back to the command window approach.
Warning: it heavily relies on undocumented features and may break in any feature release. Tested on Matlab 2015b, but should work on most previous releases as well. Some may say it is quite dirty, but it works very well, and it's the only method that I'm aware of to achieve such a behavior.
function [className, fullPath] = getStaticCallingClassName()
ST = dbstack('-completenames');
% First one is getStaticCallingClassName, second one is the superclass
methodName = char(regexp(ST(2).name, '[^\.]([^.]*)$', 'match'));
% matches string (combination of alphanumeric/underscore/dot characters) preceeding the given method call.
pattern = sprintf('[\\w.-]*(?=.%s)', methodName);
% If the parent called static method programmatically, we should be able to find it via the next (third) stack trace
if length(ST) > 2
command = evalc('dbtype(ST(3).file, num2str(ST(3).line))');
className = char(regexp(command, pattern, 'match'));
else % was likely called from command window. Long stack trace means that we're simply waiting in a breakpoint somewhere
className = []; % go straight to command window approach
end
if isempty(className) % means that static method was called directly from command window
javaHistory = com.mathworks.mlservices.MLCommandHistoryServices.getSessionHistory();
command = char(javaHistory(end));
className = char(regexp(command, pattern, 'match'));
end
fullPath = which(className);
end
Here's a workaround. According to the MATLAB documentation:
'Ordinary methods define functions that operate on objects of the class',
'Static methods are (1) associated with a class, but (2) not with specific instances of that class'.
You can have both aspects of static methods if you call an ordinary method with an empty object array.
For example, suppose we have a base class:
classdef base
methods
function obj = base()
disp('constructor called')
end
function dispClassName(obj)
disp(['class name = ', class(obj)]);
end
end
end
and a subclass
classdef sub < base
end
Now call the methods as follows (this will not invoke any constructor):
>> base.empty.dispClassName
class name = base
>> sub.empty.dispClassName
class name = sub
A real solution (for which I did an enhancement request 03315500 to MathWorks) would be to extend the MATLAB language with a method attribute 'Class' to define methods that are associated with the invoking class (similar to the Python #classmethod decorator). Methods of this class would automatically receive the metaclass of the invoking function as a first argument. With such an extension we could define a base class:
% Future MATLAB syntax extension
classdef base
methods(Class) % New method attribute ‘Class’
function dispClassName(cls) % implicit argument (meta.class)
disp(['class name = ' cls.Name ]);
end
end
end
and a subclass
classdef sub < base
end
and call
>> base.dispClassName
class name = base
>> sub.dispClassName
class name = sub
I have set a path in my pathdef (C:\MyFolder\Classes) where I have the BankAccount and AccountManager class examples from MATLAB, I have just added Test to the end of each name for testing purposes.
So when in a different path I can run the line below and it works fine. I step through and see it open the BankAccountTest file.
mb = BankAccountTest(12345, 1000);
Now I have tried adding a new class say called MyFuncs to the same directory as the classes above.
classdef MyFuncs
methods(Static)
function [mynum] = test_func(intOne, intTwo)
mynum = intOne * intTwo;
end
end
end
When I try the line below
nm = MyFuncs.test_func(5, 6);
I get the error message,
Undefined variable "MyFuncs" or class "MyFuncs.test_func".
Why can it not 'see' this class but it can see the others?
Answer
Closing my Matlab and and re-opening it seems to have done the trick
In the following piece of code, the destructor of class TdcTestResult is called at the end of function add, and so method variable mTdcTestResults will become empty again.
How can I make the instance of TdcTestResult inside mTdcTestResults persistent?
classdef Tdc
properties % (Access = private)
mTestRun = TdcTestRun;
mTestResults = [];
end
methods(Access = public)
function add(obj, componentSerialNumber, testName, testVersion, paramName, unitOfMeasureCode, paramScale, paramLimitTypeCode, paramLowerLimit, paramUpperLimit, responseValue, folderPath, isFailed, isOverridden, overriddenReason)
if(nargin > 0)
obj.mTestResults = [obj.mTestResults TdcTestResult];
obj.mTestResults(end).set(componentSerialNumber, testName, testVersion, paramName, unitOfMeasureCode, paramScale, paramLimitTypeCode, paramLowerLimit, paramUpperLimit, responseValue, folderPath, isFailed, isOverridden, overriddenReason);
obj.mTestRun.addTestResult(obj.mTestResults(end));
end
end
end
end
I think the actual issue is that you're effectively not changing obj.
If you would, the reference to the new TdcTestResult inside mTestResults should prevent the destructor from being called.
This is connected to Danial R's answer:
If you don't implement Tdc as a handle subclass, your add method has to return the modified obj.
Otherwise the changed you make inside add are lost.
So you'll either have to return obj and modify your calling syntax to
tdcObject = tdcObject.add(...);
or inherit from handle - which is probably the better alternative.
TdcTestResult needs handle to be a superclass, otherwise your code can not work. More details in the documentation
If this does not help, please include an executable example of code.