Cannot define a cell array of custom classes in function argument validation

1 view (last 30 days)
I've a class like the following one:
classdef Session < handle
%SESSION Summary of this class goes here
% Detailed explanation goes here
properties (Access = private)
configurations (1, :) cell {mustBeA(configurations, "App.Core.Configuration")}
end
methods (Access = public)
function this = Session()
this.configurations = {}
end
end
What I want to do is to declare che configuration property as a unidimensional cell array of a custom class, that can have 0 or more elements.
The problem is that I cannot instantiate it. When I try to do something like:
s = App.Core.Session()
I obtain the following error
Error using implicit default value of property 'configurations' of class 'App.Core.Session'. Value must be one of the following types: 'App.Core.Configuration'.
even if the App.Core.Configuration() class exists and it works (I've already unit tested it).
How can I declare correctly my configurations property?

Accepted Answer

Sandeep Mishra
Sandeep Mishra on 3 Oct 2024
Hi Daniele,
I see that you are trying to implement a cell array of a custom class with function argument validation. You can accomplish this by using either of the following two workarounds:
1. You can define a custom function argument validation by creating a separate validation function, such as the validator function in the following example:
function validator(configurations)
for i = 1: numel(configurations)
config = configurations(i);
mustBeA(config{1}, 'Configuration');
end
end
Then, you can initialize the configurations parameter using this validation function as shown:
properties (Access = private)
configurations (1,:) cell {validator}
end
2. You can create a validation check when adding a new configuration. Each configuration object will be validated during the addition process in the Session class. Below is the implementation of an addConfiguration function:
function addConfiguration(this, config)
mustBeA(config, 'Configuration');
this.configurations{end+1} = config;
end
With this approach, no validation is required in the properties section, configuration parameter can be defined as follows:
properties (Access = private)
configurations % No direct validation here
end
You can now create a Session object and add multiple Configuration class objects as required:
% Session object creation
session = Session();
% Add configuration objects (config1, config2) to the session
session.addConfiguration(config1);
session.addConfiguration(config2);
For more information, refer to the following MathWorks Documentation:
  1. Argument validation for cell arrays: https://www.mathworks.com/matlabcentral/answers/2018441-argument-validation-for-cell-arrays#answer_1305481
  2. Define Validation Functions: https://in.mathworks.com/help/releases/R2022b/matlab/matlab_prog/argument-validation-functions.html#mw_3e8f7e5b-fbb5-4725-be02-4751c675a92a
  3. mustBeA’ function: https://www.mathworks.com/help/releases/R2022b/matlab/ref/mustbea.html
I hope this helps!
  1 Comment
Daniele Lupo
Daniele Lupo on 3 Oct 2024
Thanks. I'll go with the first option, but usually I also check the arguments of function/methods, even if I prefer the arguments method since from the semamtic point of view the intent of the code is clearer:
function addConfiguration(this, config)
arguments
this (1, 1) Session
config (1, 1) App.Core.Configuration
end
this.configurations{end+1} = config;
end

Sign in to comment.

More Answers (2)

Matt J
Matt J on 3 Oct 2024
Edited: Matt J on 3 Oct 2024
classdef Session < handle
%SESSION Summary of this class goes here
% Detailed explanation goes here
properties (Access = private)
configurations (1, :)
end
methods (Access = public)
function this = Session()
this.configurations = {};
end
function set.configurations(obj,val)
inputValid=iscell(val);
if inputValid && ~isempty(val)
inpuValid = all( cellfun( @(z)isa(z,'App.Core.Configuration') , val) );
end
assert(inputValid,"property value must be a cell array of 0 or more Configuration objects")
end
end

Steven Lord
Steven Lord on 3 Oct 2024
What's the default value for your property?
properties (Access = private)
configurations (1, :) cell {mustBeA(configurations, "App.Core.Configuration")}
end
The code you've written looks like the definition of Prop1 in the first code block on this documentation page. "The property definition does not specify a default value, so MATLAB initializes the property value to an empty double ([])." [Though actually that's not quite correct since you're using class validation as well, so as per the "Specify Valid Default" section on this documentation page, "If you do not specify a default value, MATLAB creates a default value by assigning an empty object of the specified class or by calling the default constructor if size restriction does not allow the use of an empty default value."]
Is that a valid value for your property? Since it is not (it fails the property validation function check), MATLAB correctly throws an error.
Error using implicit default value of property 'configurations' of class 'App.Core.Session'. Value must be one of the following types: 'App.Core.Configuration'.
Depending on the details of what the App.Core.Configurations class actually is, using App.Core.Configuration.empty() along the lines of the Prop4 definition might be an option (and defining configurations to be an array of that class rather than a cell array.) Though if it is a handle object as I suspect it is, see the "Handle Objects as Default Property Values" section on the first documentation page to which I linked.
  2 Comments
Daniele Lupo
Daniele Lupo on 3 Oct 2024
Edited: Daniele Lupo on 3 Oct 2024
the default value is an empty cell array, I define it in the constructor. this logic works with other object types like the string, but not for my custom class.
Steven Lord
Steven Lord on 3 Oct 2024
configurations = cell(1, 0);
try
mustBeA(configurations, "App.Core.Configuration")
fprintf("mustBeA succeeded")
catch ME
fprintf("mustBeA threw error:\n%s", ME.message)
end
mustBeA threw error: Value must be one of these types: 'App.Core.Configuration'.
A cell array is not an App.Core.Configuration.
Even if you had a cell array containing an instance of a class, that doesn't make the cell array be an instance of that class. mustBeA checks for an is-a relationship not a contains-a relationship.
g = graph;
try
mustBeA(g, "graph") % Succeeds, g isa graph
fprintf("mustBeA succeeded")
catch ME
fprintf("mustBeA threw error:\n%s", ME.message)
end
mustBeA succeeded
gc = {g};
try
mustBeA(gc, "graph") % fails, gc isnota graph. gc containsa graph.
fprintf("mustBeA succeeded")
catch ME
fprintf("mustBeA threw error:\n%s", ME.message)
end
mustBeA threw error: Value must be one of these types: 'graph'.
this logic works with other object types like the string, but not for my custom class.
You don't wrap a string in a cell array in your object. If you did you'd have the same contains-a versus is-a problem.
s = "abracadabra"; % is-a
try
mustBeA(s, "string")
fprintf("mustBeA succeeded")
catch ME
fprintf("mustBeA threw error:\n%s", ME.message)
end
mustBeA succeeded
sc = {s}; % contains-a
try
mustBeA(sc, "string")
fprintf("mustBeA succeeded")
catch ME
fprintf("mustBeA threw error:\n%s", ME.message)
end
mustBeA threw error: Value must be one of these types: 'string'.

Sign in to comment.

Categories

Find more on Handle Classes in Help Center and File Exchange

Products


Release

R2022b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!