Being given the name of a variable as a string (in my case the name of an existing Simulink.Parameter variable in the workspace selected by the user as a design variable for optimization), I would like to be able to access the properties of the object such as Simulink.Parameter.Min, Simulink.Parameter.Max, Simulink.Parameter.Value without using eval(). So far I am employing the (very ugly) solution
varnames = {'var1','var2'}; % Simulink.Parameter objects existing in workspace
objects = cell(length(varnames),1);
for i = 1:length(varnames)
eval(['objects{i}=', varnames{i}, ';']) % Store objects in a cell array
end
Ideally, this would look like:
objects = get_object_handles_from_string(varnames);
value_1 = object{1}.Value(:);
Otherwise a method returning the variable name given the object handle would also be acceptable.
Methods that I found not to be working but might be useful otherwise:
whos finds variable names and properties in the current workspace but no handles.
inputname returns the variable name of an explicit function input as a string but does not work for cell arrays of objects (see this question).
str2func returns a function handle with a string as input but does not enable access to attributes.
findobj returns objects given an array of objects to iterate over which I do not have. Might there be a method returning all workspace variable handles as an array?
Thanks!
This is exactly what eval is for. Yes, you should avoid using eval, but if you want to have a user type in stuff to be evaluated, you need eval. Or evalin if you want to evaluate it in the base or caller workspace rather than the current workspace.
There are no such thing as "object handles" (except graphics objects, but that is not what you are talking about here). There are variables owning arrays of data, that is it.
If you don't trust your users, don't use eval. They could type in anything, including clear all or !\rm -rf /* (or whatever the Windows equivalent is to erase the disk).
In this case, and presuming there is a limited set of variables that the user can specify, do
var1 = 1;
var2 = 2;
varnames = {'var1','var2'}; % Simulink.Parameter objects existing in workspace
objects = cell(size(varnames));
for i = 1:numel(varnames)
objects{i} = get_variable_value(varnames{i}) % Store objects in a cell array
end
function val = get_variable_value(name)
switch name
case 'var1'
val = evalin('caller',var1);
case 'var2'
val = evalin('caller',var2);
otherwise
error('Illegal variable name')
end
Related
In my example, I have a class representing a certain set of data. Some of the data's properties are written to the class' properties, while the class' methods mostly perform tasks like reading the raw data from spreadsheets and doing some pre-calculations.
I was wondering if the following is according to the "pure doctrine" of OOP, and if not, if and how it could be syntactically improved.
Here is a very simple (untested) implementation of such a class.
classdef dataObjectTest < handle
properties
filename char
pathFilename char
rawData double
end
methods
function obj = setPathnameFromFilename(obj, filename)
%setPathnameFromFilename Determine full path- & filename from single filename
% filename: Name of the file containing the raw data, e.g., 'Test.xls'
obj.filename = filename;
intermed = dir(obj.filename);
obj.pathFilename = fullfile(intermed.folder, obj.filename);
end
function obj = loadRawDataFromSpreadsheet(obj)
% loadRawDataFromSpreadsheet Convert the raw data stored in a spreadsheet into a Matlab
% array, using the full path- & filename determined by setPathnameFromFilename
% rawData: Array contating the raw data
obj.rawData = xlsread(obj.pathFilename);
end
end
end
And here is how one would call this class from another program/script:
test = dataObjectTest;
test.setPathnameFromFilename('Test.xls');
test.loadRawDataFromSpreadsheet;
I am especially concerned about the third line. Since both input and output arguments of the method loadRawDataFromSpreadsheet are properties of the class, it is unnecessary to explicitly define them in the function header. On the other hand, this implementation seems somehow awkward to me and not in "spirit" of OOP, which is a lot about clearly defined interfaces between the user and the class.
So although my code works, I'm still wondering if this is "the right way" to do it or if could be significantly improved.
UPDATE: Admittedly, the choice of my methods' names may have been misleading, but this was not the point of the question. So let me try to clarify.
The first method takes the user-given string filename and returns pathFilename. But since this is also a property of the class, it is not visible from the function signature, i.e., the function should look like:
function pathFilename = setPathnameFromFilename(obj, filename)
But this doesn't work in Matlab. (The varName = part corresponds to return varName in other languages.)
The second method returns the array rawData, and since the input argument pathFilenameis, again, a property of the class, neither are visible in the method's signature.
So my concern was about having methods which actually have in- and output arguments, but do not reveal them through their signature.
It's basically fine, but I'd implement it like this:
classdef dataObjectTest < handle
properties
filename char
pathname char
rawData array
end
methods
function loadRawDataFromSpreadsheet(obj, filename)
obj.filename = filename;
obj.pathname = dataObjectTest.extractPathName(filename
obj.rawData = xlsread(obj.pathname);
end
end
methods (Static, Access = private)
function pathname = extractPathName(filename)
intermed = dir(filename);
pathname = fullfile(intermed.folder, filename);
end
end
end
Notes:
Since it's a handle object, you don't need obj as an output argument of the main method.
I've moved the extraction of the path to a private static method, as it's really a utility function (you could instead implement as an actual subfunction rather than a method).
I've renamed the main method to start with load rather than get, to make sure it isn't mistaken for a property get method.
I wouldn't worry too much about the "spirit of OOP" - instead, just make sure your code works, is well-organised, is testable, and maintainable.
It is just fine according to the "doctrine" of pure OOP for a method to have no in or out parameters apart from the object reference.
However, what you are doing doesn't conform to normal practice for method naming. A method with a name starting with get is normally a "getter". The primary purpose of a "getter" is to return some component of the class. Generally speaking, a "getter" should not modify the target object.
But you have two get... methods that 1) modify the target object and 2) don't return anything.
In my opinion:
the first one should be named setPathnameFromFilename or maybe just setFilename.
the second one should be named loadRawDataFromSpreadsheet or something like that.
On the other hand, this implementation seems somehow awkward to me and not in "spirit" of OOP, which is a lot about clearly defined interfaces between the user and the class.
I actually don't see that at all. Sure, the interface is not clearly defined, but that is largely because you have not documented the methods and because you have chosen (IMO) misleading / non-informative class and method names.
(Disclaimer: I am a Java programmer. However, the basic principles of OOP are largely the same for all OO languages.)
I am facing a strange behaviour in MATLAB when declaring an object variable as global. Where all global doubles I define seem to be recognizable by functions, it seems not possible to do the same with objects, or structs of objects that are defined from a class. This is what I have done.
Class definition:
classdef Class < handle
properties
property
end
methods
function obj = Constructor(value)
if nargin == 1
obj.property = value;
end
end
end
end
Construction of objects array:
% Define objects array as global variable to be accessible in functions
global objectsArray variableA
% define value of variable A, to be accessible in functions
variableA = 123;
% Values to be assigned to objects' property field
values = [1,2,3];
% initialise object array using constructor
if isempty(objectsArray)
objectsArray(3,1) = Constructor();
end
% define objects' property field
for k = 1:3
objectsArray(k) = Constructor( values(k) );
end
Now, I'd like a function to access both the objects array, and variable A, both defined above, and both declared as global variables. However, if I build a function as follows, only variable A is accessible, while the objects array is seen as an empty variable.
function varargout = doSomething (varargin)
global objectsArray variableA
aaa = variableA;
bbb = objcetsArray(1).property; <--- code crashes here
end
The code crashes at executing the instruction defining bbb. Debug mode reveals that objectsArray is empty, and that variableA is equal to its value, 123. Ending debug mode, exiting therefore the function's execution, the objectsArray is defined as I'd expect, as a structure of 3 objects.
My question is, why does this happen? Why does it seem possible to recall global variables, but not if they are defined as objects?
After some research, I realised the problem was not the object itself, but the concept of global variable and how MATLAB works with it.
The same issue materializes when trying to make a struct global.
An answer to my question is explained in this thread by the user Steven Lord:
The problem here is not caused by global, at least not directly. When
you define a variable to be global, it starts out as an empty
(0-by-0) double array. So this line:
global array_Structure
sets array_Structure to be a global array containing [].
Now when you try to assign a struct array (data_Struct) into an
element of the array_Structure double variable, since the variable
has to be all one type, MATLAB has to convert one of the variables
into the other's type. In this case, since you're trying to put the
struct into the double array, it tries to convert the struct into a
double array. For some pairs of the built-in data types, MATLAB knows
how to convert between the two data types implicitly (for instance,
double to single) -- but struct and double is not one of those pairs
of types, so MATLAB says that converting a struct into a double is
not possible. It doesn't know how to do it.
I would like to have a class which, in its constructor, can have knowledge (extract as a string) its instance name.
For the moment I worked the name extraction out like this:
classdef mysession
methods (Access = public)
function this=mysession (varargin)
this.cargs=varargin;
this.built=false;
end
function id=build(this)
id=this.mynameis;
this.id = id;
%% instructions needing id
built=true;
end
function name = mynameis (this)
name=evalin ('caller', 'inputname');
end
end
properties (Access=private)
id
built
cargs
end
end
which requires the ugly
A = mysession; A.build
syntax in order to work...
There is no way to get the variable name that is used to assign the output of a function or class constructor. As you've discovered, the only way to get the object's variable name in the calling workspace is to call another method of the class at which point you can use inputname to query it.
That aside, it's not clear why you need to do this but I'd strongly discourage it. Particularly with handle classes, you can have multiple variables point to the same object, therefore the object technically has multiple names.
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 am having a go at matlab object orientated programming. I have a class with properties. The properties are computed if they are NaN otherwise the property is returned.
So I have this section:
properties
some_property = NaN;
end
and a property is computed like this:
function some_property = get_some_property(obj)
if(isnan(obj.some_property))
% do some expensive computation
obj.some_property = ...;
end
some_property = obj.some_property;
A get_some_property method can be used by another get_some_property method. The problem is that between method calls properties are set back to NaN. This causes the error:
Maximum recursion limit ...
Any ideas? Thanks.
Your problem is that your class is not derived from handle, so it's passed around as a value. This means that when you call get_some_property, Matlab faithfully duplicates obj, giving a copy to the function get_some_property and keeping that separate from the copy of the code that called get_some_property. So, when you change some_property in the function get_some_property, it changes the property on the value of obj in the function, but not the property of obj in the code that called it.
There are two solutions to your problem. The best solution (IMO) is to derive your class from handle. Classes derived from handle are passed as references, so when you change the property on obj, it will change the property everywhere.
However, if you're unwilling to do this, then you can return obj from get_some_property:
function [some_property,obj] = get_some_property(obj)
And then call get_some_property in this way:
[property,obj] = get_some_property(obj);
This updates the value of obj in the calling namespace. It's both awkward and clunky; I would derive your classes from handle unless there's a good reason to avoid it.