I tried to change an attribute value of a class by invoking one of its member function:
p1 = tank();
p1.checkOri(p1);
And in the class definition, I have that static method:
classdef tank
properties
value
...
end
methods
...
methods (Static)
function obj = checkOri(obj)
if (CONDITION) %the thing I want it to do
obj.value = EXPRESSION;
...
Yet this checkOri method is not working. But if I write this method in the main file, or to say altering the value of p1-the instance of class tank-it works perfectly:
p1 = tank();
p1.checkOri(p1);
if (CONDITION) %the thing I want it to do
p1.value = EXPRESSION;
It works perfectly.
I wonder what caused this. From my experience with other programming languages, invoking method should have worked, is it because of some tricks with Matlab syntax or static method? How could I fix it so that this method would work?
So, as #Navan in the comment said, handle class could be a solution.
It appears Matlab has a similar parameter concept with Java and C++, arguments modified in a function/method only remains that modification inside the function/method.
For this class, I simply added < handle in the head of class definition and it worked:
classdef tank < handle
properties
...
But I am not sure if that is the only solution, there might be better ways to do this. So I'll leave that question open, you are more than welcomed to post your opinion:D
In MATLAB, the call
p1.checkOri();
is equivalent to
checkOri(p1);
In both cases, the class method checkOri is called for the class of object p1, passing p1 as first argument to the function by value.
Because p1 is passed by value, any changes made to it inside the function are not seen by the object in the calling workspace. Therefore, one typically does
p1 = checkOri(p1);
This way, the object that was passed by value and modified inside the function is passed back out and assigned to the variable that held the original object.
If the method is written as follows:
function obj = checkOri(obj)
%...
end
then MATLAB will optimize the above function call such that no copy of the object is actually made. Note that both in the function declaration and in the function call, the input and output variable is the same.
As already discovered by OP, the above does not hold for handle classes, classes that inherit from handle. These classes act as if they are always passed by reference, and any change made to them in any workspace will be reflected in all other copies in other workspaces.
Also assigning to a member variable does not follow the above, such that
p1.value = 0;
modifies object p1.
For more information on the difference between value classes and handle classes see this other question.
Related
I have a class, for which I have static methods that calculate something (example below in MATLAB, since that's what I'm using, although I think my question may be more general):
methods (Static)
function out = f(x)
out = x*2;
end
end
Suppose I have instance methods where I want to do the same calculation (and hence use the aforementioned class method), but I also want to do something specific to the class, like updating a property or maybe passing in an instance property into the calculation. So like:
methods
function out = f(this)
out = ClassName.f(this.x); % Use the class method, but pass in an instance variable
end
% OR
function [this, out] = f(this,x)
out = ClassName.f(x)*2; % Use the class method with a provided input
this.value = out; % then assign value to an instance variable
end
end
At least in MATLAB, I can't overload a class method with an instance method (I can overload a built-in MATLAB method within my class, but that's not what I want here), so I'm not sure how to go about this in a proper OOP way. I want to keep the class method there in case someone wants to use the specific calculation on some data they collected outside the class, but I also want the instance method in case someone decides to use the class to collect data and then analyze the data with the built methods.
Within a subclass constructor, what is the difference between calling obj#SuperClass(a,b); and obj = obj#SuperClass(a,b);
Both are found in doc, for ex. here
The canonical syntax for calling a superclass constructor is
obj = obj#SuperClass(args)
(see the documentation).
This is comparable to calling any constructor, where the output of the function call is assigned to a variable:
obj = SuperClass(args)
However, because the superclass constructor must initialize fields inside a (larger) derived object, the obj to be modified must be passed in some way to the function call, hence the (awkward) obj# syntax.
But because we pass the object to be initialized, which is modified, we don’t really need to capture that output any more. Hence the other form,
obj#SuperClass(args)
does exactly the same things in all situations I have encountered.
There is no difference that I can see. I would be surprised if the first syntax did any data copying whatsoever, that has most certainly been optimized out, just like obj = obj.method(args) doesn’t copy the object.
I defined a class in one of my many MATLAB packages. To my suprise, I could not access a constant property of my class without importing the class definition. Even if it is a method of the class itself. Like so:
classdef TestClass
properties( Constant )
c=0;
end
methods
function obj = TestClass()
end
function getC(obj)
import test.TestClass;
disp(TestClass.c);
end
end
end
I just want to check whether I am doing something wrong here or is this the correct way to use constants in MATLAB.
Since you have placed TestClass inside a package Matlab needs to know where to look to find the definition for this class, even if it's a reference from within the class or function. An alternate to the above code could be:
function getC(obj)
disp(test.TestClass.c);
end
Alternately, if within a class, constant values can be accessed from the object itself.
function getC(obj)
disp(obj.c);
end
If neither of these is working for you, you may need to refresh the classdef for TestClass from memory. This will cause matlab to reload the constant value, which is pulled into Matlab when it first parses the classdef file to determine the structure of the class. This can be done using clear classes, however a warning that it will also clear all other classes, variables, and any breakpoints you have set.
If you want to see if this is necessary you can view the metaclass object to determine what Matlab "thinks" your class structure should be. You can do this using the following.
mc = ?test.TestClass;
mc.PropertyList
You may need to index into the property list to find the specific property you are interested in, but the thing you are looking for are the following fields.
Name: 'c'
Constant: 1
DefaultValue: 0
I'm trying to write a class that wraps around a serial port to read a sensor:
classdef sensor < handle
properties
Value
s
end
methods
function obj = sensor(port)
obj.s = serial(port);
obj.s.BytesAvailableFcn = #(o,e) obj.getData;
fopen(obj.s);
end
function delete(obj)
disp('called destructor');
try
fclose(obj.s);
delete(obj.s);
end
end
function getData(obj)
obj.Value = fscanf(obj.s, '%d');
end
end
end
When I try to clear the workspace, the destructor never gets called:
>> foo = sensor('COM1');
>> clear foo % should disp() something!
Based on my previous experiences, there must still be a reference to foo. Turns out this is embedded in the serial port foo.s:
>> ports = instrfindall;
>> ports.BytesAvailableFcn
ans =
#(o,e)obj.getData
Once I clear out the BytesAvailableFcn, i.e.,
>> ports.BytesAvailableFcn = '';
and then clear all, I get my called destructor displayed.
How can I break this circular reference and get my destructor to get called? If the destructor doesn't get called, the serial port stays bound.
The problem is not quite that you have a circular reference; MATLAB should in theory catch those. The issue is that the circular reference goes via Java, and MATLAB can't catch it.
In more detail - your serial object contains a Java object internally, which is the real thing that captures the connection to the serial port. When you set its callback, MATLAB sets the callback of the underlying Java object. If the callback is to a method of an object that contains the serial object as a property, then you have a circular reference that goes via the underlying Java object. When you call clear, MATLAB checks for circular references, and if they were all in only MATLAB it would catch them, and call the destructor - but it doesn't catch them as it goes via Java.
One solution is to explicitly call the destructor, and perhaps that's not too much trouble.
Another workaround might be to simply not have the callback as a method of the class - perhaps it could just be a regular function, as it doesn't really seem to need any information from the main object itself other than the reference to the serial object. If you would prefer to keep all the code in a single file, perhaps you could create it as an anonymous function (although be careful, as an anonymous function will capture the workspace that it's created in, which depending on where you create it may include a reference to the main object, in which case you'd have a circular reference again.
Edit: Rody's right - my apologies, I read the problem too fast and didn't notice that getData actually sets the Value property rather than just returning the data. Rody's solution in his answer may be an alternative workaround, but like he says - yuck. I'd just call delete manually.
Intriguing problem! :)
I found a workaround, but it's not going to be pretty.
You cannot use an anonymous function. That would capture the local workspace, which will contain a reference to obj. You'll have to use one level of indirection (to a Static method, otherwise, you'd have the same problem).
This static method can safely return a function handle. This function handle is to a function, which has to be passed the instrument object.
Setting the Value property is of course impossible without passing a reference to the object, so, you'll have to create a global state in the function, a variadic call signature, and a getter for the Value property.
I have the feeling I've over-engineered this a bit (it's Friday after all), so if anyone sees a simpler way, please correct me. Anyway, here's what I mean:
classdef sensor < handle
properties
s
end
properties (Dependent)
Value
end
methods
function obj = sensor(port)
obj.s = serial(port);
% You cannot use function handle without implicitly passing obj into
% it. Instead, get a function handle from another function, one that
% does not have this problem:
obj.s.BytesAvailableFcn = sensor.getGetData(obj.s);
fopen(obj.s);
end
function delete(obj)
disp('called destructor');
try %#ok<TRYNC>
fclose(obj.s);
delete(obj.s);
end
end
% Use a getter for Value, so that whenever you query the Value property,
% you get the most recently read sensor data
function V = get.Value(obj) %#ok<MANU>
V = getData();
end
end
% Use indirection to a Static method, to avoid referencing obj implicitly
methods (Access = private, Static)
function f = getGetData(S)
f = #(o,e)getData(S);
end
end
end
% The actual sensor reader
function data = getData(S)
persistent port
if nargin == 1
port = S; return; end
try
data = fscanf(port, '%d');
catch ME
% ...
end
end
Just....yuck.
I'm having some trouble creating classes in matlab and I don't really understand the method behind it (i'm fairly new to it)
here is my attempt at basic addition using matlab
classdef test
properties
a
b
end
methods
function add = plus(a, b)
end
end
end
assigning values via
p=test(), p.a=5
etc seems to work fine, however attempting p.add returns the error
No appropriate method, property, or field add for class test.
Any help or guidance would be appretiated, thanks.
Methods are defined exactly as functions are with respect to names and outputs.
Therefore, the method is called plus, the output the method should calculate is called add, and the way you probably wanted to write the method is:
function out = add(this)
out = this.a + this.b;
end
Now you call the method as
p.add();