In Matlab, I can define a class as such:
classdef klass < handle
properties(Dependent)
prop
end
end
Matlab is perfectly happy instantiating an object of this class, even without defining a getter for prop. It only fails when I try to access it (understandably). I'd like to set the GetMethod dynamically based upon the property's name.
Unfortunately, even when the property is Dependent, the meta.property field for GetMethod is still read-only. And while inheriting from dynamicprops could allow adding a property and programmatically setting its GetMethod in every instance, I don't believe it could be used to change an existing property. I may have to go this route, but as prop must exist for every object I'd prefer to simply set the getter on a class-by-class basis. Is such a thing possible?
An alternative solution could be through some sort of catch-all method. In other languages, this could be accomplished through a Ruby-like method_missing or a PHP-like __get(). But as far as I know there's no (documented or otherwise) analog in Matlab.
(My use case: this class gets inherited by many user-defined subclasses, and all their dependent properties are accessed in a similar way, only changing based on the property name. Instead of asking users to write get.* methods wrapping a call to the common code for each and every one of their dependent properties, I'd like to set them all dynamically with anonymous function pointers containing the necessary metadata).
Here is my proposal: create a method in the superclass called add_dyn_prop. This method is to be called in the subclasses instead of creating a dependent property the usual way.
The idea is that the superclass inherit from dynamicprops and use addprop to add a new property, and set its accessor methods manually based on its name.
classdef klass < dynamicprops
methods (Access = protected)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
% NOTE: this has to be a simple function_handle (#fun), not
% an anonymous function (#()..) to avoid infinite recursion
p.GetMethod = #get_method;
p.SetMethod = #set_method;
% nested getter/setter functions with closure
function set_method(obj, val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
now in the subclass, instead of defining a dependent property the usual way, we use this new inherited function in the constructor to define a dynamic property:
classdef subklass < klass
%properties (Dependent, SetAccess = private)
% name
%end
%methods
% function val = get.name(obj)
% val = 'Amro';
% end
%end
methods
function obj = subklass()
% call superclass constructor
obj = obj#klass();
% define new properties
add_dyn_prop(obj, 'name', 'Amro');
add_dyn_prop(obj, 'age', [], false)
end
end
end
The output:
>> o = subklass
o =
subklass with properties:
age: []
name: 'Amro'
>> o.age = 10
o =
subklass with properties:
age: 10
name: 'Amro'
>> o.name = 'xxx'
You cannot set the read-only property 'name' of subklass.
Of course now you can customize the getter method based on the property name as you initially intended.
EDIT:
Based on the comments, please find below a slight variation of the same technique discussed above.
The idea is to require the subclass to create a property (defined as abstract in the superclass) containing the names of the desired dynamic properties to be created. The constructor of the superclass would then create the specified dynamic properties, setting their accessor methods to generic functions (which could customize their behavior based on the property name as you requested). I am reusing the same add_dyn_prop function I mentioned before.
In the subclass, we are simply required to implement the inherited abstract dynamic_props property, initialized with a list of names (or {} if you dont want to create any dynamic property). For example we write:
classdef subklass < klass
properties (Access = protected)
dynamic_props = {'name', 'age'}
end
methods
function obj = subklass()
obj = obj#klass();
end
end
end
The superclass is similar to what we had before before, only now is it its responsibility to call the add_dyn_prop in its constructor for each of the property names:
classdef klass < dynamicprops % ConstructOnLoad
properties (Abstract, Access = protected)
dynamic_props
end
methods
function obj = klass()
assert(iscellstr(obj.dynamic_props), ...
'"dynamic_props" must be a cell array of strings.');
for i=1:numel(obj.dynamic_props)
obj.add_dyn_prop(obj.dynamic_props{i}, [], false);
end
end
end
methods (Access = private)
function add_dyn_prop(obj, prop, init_val, isReadOnly)
% input arguments
narginchk(2,4);
if nargin < 3, init_val = []; end
if nargin < 4, isReadOnly = true; end
% create dynamic property
p = addprop(obj, prop);
%p.Transient = true;
% set initial value if present
obj.(prop) = init_val;
% define property accessor methods
p.GetMethod = #get_method;
p.SetMethod = #set_method;
% nested getter/setter functions with closure
function set_method(obj,val)
if isReadOnly
ME = MException('MATLAB:class:SetProhibited', sprintf(...
'You cannot set the read-only property ''%s'' of %s', ...
prop, class(obj)));
throwAsCaller(ME);
end
obj.(prop) = val;
end
function val = get_method(obj)
val = obj.(prop);
end
end
end
end
Note: I did not use ConstructOnLoad class attribute or Transient property attribute, as I am still not sure how they would affect loading the object from a saved MAT-file in regards to dynamic properties.
>> o = subklass
o =
subklass with properties:
age: []
name: []
>> o.name = 'Amro'; o.age = 99
o =
subklass with properties:
age: 99
name: 'Amro'
Check if this is what you want. The problem is that the user will need to get the properties using (), which may be quite boring, but anyway, I think this way you can change the variables. You can't change them directly on the class, but you can change the objects property values on demand. It doesn't need to change the values on the constructor, you can do that using another function that will be inherited by the classes.
klass1.m
classdef(InferiorClasses = {?klass2}) klass < handle
methods
function self = klass
selfMeta = metaclass(self);
names = {selfMeta.PropertyList.Name};
for name = names
switch name{1}
case 'prop_child_1'
self.(name{1}) = #newGetChild1PropFcn;
case 'prop_child_2'
self.(name{1}) = #newGetChild2PropFcn;
end
end
end
end
methods(Static)
function out = prop
out = #defaultGetPropFcn;
end
end
end
function out = defaultGetPropFcn
out = 'defaultGetPropFcn';
end
function out = newGetChild1PropFcn
out = 'newGetChild1PropFcn';
end
function out = newGetChild2PropFcn
out = 'newGetChild2PropFcn';
end
klass2.m
classdef klass2 < klass
properties
prop_child_1 = #defaultGetChildPropFcn1
prop_child_2 = #defaultGetChildPropFcn2
end
methods
function self = klass2
self = self#klass;
end
end
end
function out = defaultGetChildPropFcn1
out = 'defaultGetChildPropFcn1';
end
function out = defaultGetChildPropFcn2
out = 'defaultGetChildPropFcn2';
end
Output:
a = klass2
b=a.prop_child_1()
b =
newGetChild1PropFcn
Related
There are dependent properties in class A, based on an argument in constructor I want to make some of properties Hidden, so that user will not be able to set/get these properties.
classdef A
properties (Dependent = true)
prop1
prop2
end
methods
function value = get.prop1(obj)
...
end
function value = get.prop2(obj)
...
end
end
methods(Access = public)
function obj = A(arg1)
if arg1 == 1
% make prop1 Hidden for the constructed object
end
end
end
end
and here is sample usages:
a1 = A(2);
a1.prop1; % ok
a2 = A(1);
a2.prop1; % problem, user will not know about existence prop1
The Access level is fixed, as in any OOP language I know. It is fundamental to how the class interacts with other code.
Your only workaround is to use Dependent properties of a matlab.mixin.SetGet type class, and have a conditional behaviour based on the construction argument. Here is a POC class to demonstrate:
Class:
classdef POC < matlab.mixin.SetGet
properties ( Dependent = true )
prop
end
properties ( Access = private )
arg % Construction argument to dictate obj.prop behaviour
prop_ % Private stored value of prop
end
methods
function obj = POC( arg )
% constructor
obj.prop = 'some value'; % Could skip setting this if argCheck fails
obj.arg = arg;
end
% Setter and getter for "prop" property do obj.argCheck() first.
% This throws an error if the user isn't permitted to set/get obj.prop
function p = get.prop( obj )
obj.argCheck();
p = obj.prop_;
end
function set.prop( obj, p )
obj.argCheck();
obj.prop_ = p;
end
end
methods ( Access = private )
function argCheck( obj )
% This function errors if the property isn't accessible
if obj.arg == 1
error( 'Property "prop" not accessible when POC.arg == 1' );
end
end
end
end
Output
>> A = POC(1);
l>> A.prop
Error using POC/get.prop (line 17)
Property "prop" not accessible when POC.arg == 1
>> A = POC(2);
>> A.prop
ans =
'some value'
Edit: I'd create two different classes, one with hidden and one with visible attributes.
In that case you can just set the corresponding property attributes in the hidden class. This should do it:
properties (Dependent = true, Hidden = True, GetAccess=private, SetAccess=private)
Matlab does not allow to define different methods to define multiple constructors with different list of parameters, for instance this will not work:
classdef MyClass
methods
function [this] = MyClass()
% public constructor
...
end
end
methods (Access = private)
function [this] = MyClass(i)
% private constructor
...
end
end
end
But, as illustrated in above example, it is sometimes useful to have private constructors with particular syntax that cannot be called from public interface.
How would you best handle this situation where you need to define both public and private constructors ?
Checking call stack ???
classdef MyClass
methods
function [this] = MyClass(i)
if (nargin == 0)
% public constructor
...
else
% private constructor
checkCalledFromMyClass();
...
end
end
end
methods(Static=true, Access=Private)
function [] = checkCalledFromMyClass()
... here throw an error if the call stack does not contain reference to `MyClass` methods ...
end
end
end
Define a helper base class ???
% Helper class
classdef MyClassBase
methods (Access = ?MyClass)
function MyClassBase(i)
end
end
end
% Real class
classdef MyClass < MyClassBase
methods
function [this] = MyClass()
this = this#MyClassBase();
end
end
methods (Static)
function [obj] = BuildSpecial()
obj = MyClassBase(42); %%% NB: Here returned object is not of the correct type :( ...
end
end
end
Other solution ???
One sneaky trick that I've used to try and work around this limitation is to use another 'tag' class that can only be constructed by MyClass, and then use that to work out which constructor variant you need. Here's a simple sketch:
classdef MyClass
properties
Info
end
methods
function o = MyClass(varargin)
if nargin == 2 && ...
isa(varargin{1}, 'MyTag') && ...
isnumeric(varargin{2}) && ...
isscalar(varargin{2})
o.Info = sprintf('Private constructor tag: %d', varargin{2});
else
o.Info = sprintf('Public constructor with %d args.', nargin);
end
end
end
methods (Static)
function o = build()
% Call the 'private' constructor
o = MyClass(MyTag(), 3);
end
end
end
And
classdef MyTag
methods (Access = ?MyClass)
function o = MyTag()
end
end
end
Note the Access = ?MyClass specifier which means that only MyClass can build instances of MyTag. There's more about using that sort of method attribute in the doc: http://www.mathworks.com/help/matlab/matlab_oop/method-attributes.html
You can have a constructor that accepts multiple syntaxes by taking advantage of varargin:
classdef MyClass
methods (Access = public)
function obj = MyClass(varargin)
% Do whatever you want with varargin
end
end
end
You might, more typically, have some inputs that are required for all syntaxes, and then some optional inputs as well:
classdef MyClass
methods (Access = public)
function obj = MyClass(reqInput1, reqInput2, varargin)
% Do whatever you want with varargin
end
end
end
If you want to have even more control over how things are constructed, I would have a constructor and then also have some public static methods that called the constructor.
For example, let's say I wanted to be able to construct an object either by supplying parameters directly, or by supplying the name of a config file containing parameters:
classdef MyClass
methods (Access = public)
function obj = MyClass(reqInput1, reqInput2, varargin)
% Do whatever you want with varargin
end
end
methods (Static, Access = public)
function obj = fromFile(filename)
myparams = readmyconfigfile(filename);
obj = MyClass(myparams.reqInput1, myparams.reqInput2, ...);
end
end
end
Then you can create an object either with o = MyClass(inputs) or o = MyClass.fromFile(filename).
If you wanted to allow people to construct only from a config file, you could then make the constructor private. And you could add additional public static methods if you wanted to call the constructor in other ways.
In any case, the main point is that the idiomatic way to have a constructor that accepts multiple syntaxes is to take advantage of varargin.
I'd be tempted to go for a modification on your 1st option, but to modify the constructor to have an undocumented "mode" rather than just any input arg.
In the example below I gave that name **private** but you could have anything you want that the end users are unlikely to stumble across....
To be doubly sure you could still check the stack.
function [this] = MyClass(i)
if (nargin == 0)
% public constructor
else
% private constructor
if ischar ( i ) && strcmp ( i, '**private**' )
this.checkCalledFromMyClass();
else
error ( 'MyClass:Consturctor', 'Error calling MyClass' );
end
end
end
end
I need some help for using varargin to assign properties in handle class.
Instead of using handle class, if I create a function with varargin, it works.
function out = testFunction(varargin)
% default values
startDate = '2011-11-01';
endDate = datestr(now,'yyyy-mm-dd');
% Map of parameter names to variable names
params_to_variables = containers.Map({'StartDate','EndDate'}, {'startDate','endDate'});
v = 1;
while v <= numel(varargin)
param_name = varargin{v};
if isKey(params_to_variables,param_name)
assert(v+1<=numel(varargin));
v = v+1;
% Trick: use feval on anonymous function to use assignin to this workspace
feval(#()assignin('caller',params_to_variables(param_name),varargin{v}));
else
error('Unsupported parameter: %s',varargin{v});
end
v=v+1;
end
end
But my current task is to create a super-class with many properties. There are various types of sub-classes. Different sub-class may need to use different properties. So when I create an instance of a super-class or sub-class, I need the input to be very flexible. As the example above, one sub-class only need the property 'startDate', and the other sub-class only need the property 'endDate'. Here is my code.
classdef superclass < handle
properties
startDate
endDate
end
methods
function obj = superclass(varargin)
% Map of parameter names to variable names
params_to_variables = containers.Map({'StartDate','EndDate'}, {'startDate','endDate'});
v = 1;
while v <= numel(varargin)
param_name = varargin{v};
if isKey(params_to_variables,param_name)
assert(v+1<=numel(varargin));
v = v+1;
feval(#()assignin('caller',params_to_variables(param_name),varargin{v}));
else
error('Unsupported parameter: %s',varargin{v});
end
v=v+1;
end
end
end
end
Basically I just copy and paste, it doesn't work.
So I change
feval(#()assignin('caller',params_to_variables(param_name),varargin{v}));
to
feval(#()assignin('caller',strcat('obj.',params_to_variables(param_name),varargin{v}));
or
feval(#()assignin('base',strcat('obj.',params_to_variables(param_name),varargin{v}));
or
evalin('caller',...
strcat('obj.',params_to_variables(param_name),'=',num2str(varargin{v})));
or
evalin('base',...
strcat('obj.',params_to_variables(param_name),'=',num2str(varargin{v})));
None of them works. If anyone can tell me how to solve this problem or provide me an alternative way, I appreciate it. Thank you.
Just started crash coursing in Matlab OO programing and I would like to write a set method for a object that will set the value then reciprocate by setting itself in the relevant field on the other object.
classdef Person
properties
age;
sex;
priority; % net priority based on all adjustment values
adjustment; % personal adjustment value for each interest
family;
end
methods
function obj = set.sex(obj, value)
if value == 'm' || value == 'f'
obj.sex = value;
else
error('Sex must be m or f')
end
end
function obj = set.family(obj,value)
if class(value) == 'Family'
obj.family = value;
else
error('Family must be of type Family')
end
end
end
end
classdef Family
properties
husband;
wife;
children;
elders;
adjustment; % interest adjustment values
end
methods
function this = set.husband(this,person)
if class(person) == 'Person'
this.husband = person;
person.family = this;
else
error('Husband must be of type Person')
end
end
function this = set.wife(this,person)
if class(person) == 'Person'
this.wife = person;
person.family = this;
else
error('Wife must be of type Person')
end
end
end
end
So what I have to do now is:
p = Person
f = Family
f.husband = p
p.family = f
What I would like is for family and person to auto set themselves in each other:
p = Person
f = Family
f.husband = p
And Family set.husband function will set p's family value to f. Why is my code not working? As far as I can tell I'm doing what is suggested in the comments.
Edit:
After some messing around I've confirmed that "this" and "person" are objects of the correct type. Ultimately the issue is that Matlab passes by value rather then by reference. Unless anyone knows a way around that I'll answer myself when I can.
Normal objects are usually considered value objects. When they are passed to a function or a method, only the value is passed not a reference to the original object. Matlab may use a read-only referencing mechanism to speed things up, but the function or method cannot change the properties of the original object.
To be able to pass an input parameter by reference, your custom object needs to be a handle object. Simply when defining your class, inherit from handle and that should do the trick:
classdef Person < handle
and
classdef Family < handle
I am trying to create a Matlab Class, where the methods attributes are changed in the Class Constructor. The purpose of this is to hide / make visible some methods, depending on the class input.
For example:
classdef (HandleCompatible) myClass < dynamicprops & handle % & hgsetget
properties (Hidden)
myProp
end
methods (Hidden)
function obj = myClass(input)
%class constructor
%add some dynamic properties
switch input
case 1
%unknown code:
%make myMethod1 visible
case 2
%unknown code:
%make myMethod2 visible
otherwise
%unknown code:
%make myMethod1 visible
%make myMethod2 visible
end
end
end
methods (Hidden)
function myMethod1 (obj, input)
%function...
end
function output = myMethod2(obj, input)
%function...
end
end
end
I tried to use the following:
mco = metaclass(obj);
mlist = mco.MethodList;
mlist(myMethod1Index).Hidden = false;
, but I get the following error:
Setting the 'Hidden' property of the 'meta.method' class is not allowed.
Thank you for your reply.
This could be a solution, if I need to access my methods selectively in the class constructor. Though, I need to use these methods in my program, and to have them visible or not, at tab completion:
%Obj1
myObj1 = myClass (inputs, '-1');
myObj1.myMethod1(arg);
%myObj1.myMethod2 - hidden
%Obj2
myObj2 = myClass (inputs, '1');
%myObj2.myMethod1 - hidden
value1 = myObj2.myMethod2(arg);
%Obj3
myObj3 = myClass (inputs, '0');
myObj3.myMethod1(arg);
value2 = myObj3.myMethod2(arg);
%here i want to be able to access both methods
Maybe it is possible to select the method properties, during class constructor, and change the attributes. But this has to be done without using the metaclass
Why not expose only a factory method and build instances of different classes depending on the input? You can use access qualifiers to lock things down like so:
% a.m
classdef a
properties, a_thing, end
methods ( Access = ?factory )
function obj = a()
end
end
end
% b.m
classdef b
properties, b_thing, end
methods ( Access = ?factory )
function obj = b()
end
end
end
% factory.m
classdef factory
methods ( Static )
function val = build(arg)
if isequal(arg, 'a')
val = a;
else
val = b;
end
end
end
end