Resolve Issue: Using arguments
Blocks to Specify Cell or Structure
Entry-Point Input Types is Not Supported
Issue
Code generation does not support the specification of cell
and
struct
input types to entry-point functions using arguments
blocks. If all of the
following conditions are true, code generation fails:
Your entry-point function accepts one or more
struct
orcell
inputs.You use the
arguments
block to specify input types.
The code generator returns one of these error messages:
Using the arguments block to specify a
'cell' input type for argument 'y' in entry-point function 'foo' is not
supported for code generation. Use -args, the Define Input Types step in the
MATLAB Coder app, or preconditioning statements instead. Alternatively, convert
the cell array to a class with property validation.
Using the arguments block to specify a
'struct' input type for argument 'y' in entry-point function 'foo' is not
supported for code generation. Use -args, the Define Input Types step in the
MATLAB Coder app, or preconditioning statements instead. Alternatively, convert
the struct to a class with property validation.
Possible Solutions
Code generation does not support the specification of cell
and
struct
input types in the arguments
block
of the entry-point function, because the types and sizes of the variables contained
within the struct
and cell
variables are
unknown by the code generator at code generation time. To work around this
limitation of code generation, you can convert the cell
or
struct
input argument to a class. Using this method, you can
continue to specify input types using the arguments
block. To
learn more about the advantages of using arguments
blocks to
specify input types, see Use Function Argument Validation to Specify Entry-Point Input Types.
Alternatively, you can use another method of input-type specification to declare
cell or structure input types. Cell and structure input types can be specified using
the MATLAB®
Coder™ app; at the command line using the codegen
function with the -args
argument; or in the body of your
MATLAB function using assert
statements. For help
deciding which of these methods of input type specification is best suited for your
use case, see Specify Types of Entry-Point Function Inputs.
Convert Single Structure Input to a Class
Consider the following function:
function out = calculationsS(calculationConfig, x, y) arguments calculationConfig (1,1) struct x (1,:) double y (1,:) double end % check config argument (calculationConfig) to determine operation type if calculationConfig.operation == myOps.mult out = x * y; elseif calculationConfig.operation == myOps.sub out = x - y; elseif calculationConfig.operation == myOps.div out = x / y; else out = x + y; end % check config argument to determine whether output should be rounded if calculationConfig.round == true out = round(out); end end
This function takes a structure with two fields, round
and
operation
, where round
is a logical
value and operation
is a custom class defined as:
classdef myOps < int8 enumeration add (0), sub (1), mult (2), div (3) end end
calculationConfig
structure are unknown by the code
generator at code generation time, and therefore code generation for
calculationsS
fails.To resolve this issue, you can easily convert operation
and
round
into properties of a custom
class:
classdef ConfigClass properties operation (1,1) MyOps round (1,1) logical end end
calculationsS
function to accept this custom class instead of the
calculationConfig
structure:function out = calculationsS(calculationConfig, x, y) arguments calculationConfig (1,1) ConfigClass x (1,:) double y (1,:) double end % check config argument (calculationConfig) to determine operation type if calculationConfig.operation == MyOps.mult out = x * y; elseif calculationConfig.operation == MyOps.sub out = x - y; elseif calculationConfig.operation == MyOps.div out = x / y; else out = x + y; end % check config argument to determine whether output should be rounded if calculationConfig.round == true out = round(out); end end
calculationsS
without further specifying input types.Convert Cell Array Input to a Class
Consider the following function:
function plotCloudCell(pointCloud) arguments pointCloud (1,:) cell end x = cellfun(@(p) p{1}, pointCloud); y = cellfun(@(p) p{2}, pointCloud); z = cellfun(@(p) p{3}, pointCloud); % Plot the points figure; scatter3(x, y, z); title("Random 3D Point Cloud"); xlabel("X"); ylabel("Y"); zlabel("Z"); end
This function takes a cell array, pointCloud
, containing
n cells of x, y,
and z coordinates:
{{x1,y1,z1}
{x2,y2,z2}
…
{xn,yn,zn}}.
However, the types and sizes of the data in the cell array are unknown by the
code generator at code generation time, and therefore code generation for
plotCloudCell
fails.
To work around this limitation of code generation, you can define a
PointsClass
, where a single instance of
PointsClass
holds all
the points in
pointCloud
:
classdef PointsClass properties xs (1,:) double ys (1,:) double zs (1,:) double end end
PointsClass
contains
three n-length arrays, such that the first point in the point
cloud is represented by x(1)
, y(1)
,
z(1)
; the second point in the point cloud is represented
by x(2)
, y(2)
, z(2)
;
and the nth point in the point cloud is represented by
x(n)
, y(n)
, z(n)
.
To convert the pointCloud
cell
array into an instance of
PointsClass
, extract the points from the
cell
array exactly as you did in the function
plotCloudCell
:pts = PointsClass pts.xs = cellfun(@(p) p{1}, pointCloud); pts.ys = cellfun(@(p) p{2}, pointCloud); pts.zs = cellfun(@(p) p{3}, pointCloud);
plotCloudCell
to accept an instance of
PointsClass
:function plotCloudClass(pts) arguments pts (1,1) PointsClass end x = pts.xs; y = pts.ys; z = pts.zs; % Plot the points using scatter3 figure; scatter3(x, y, z); title("Random 3D Point Cloud"); xlabel("X"); ylabel("Y"); zlabel("Z"); end
plotCloudClass
without further specifying input
types.Convert Structure Array Input to a Class
When the input passed to the entry-point function is a single structure, it is straightforward to represent these data as a single instance of a custom class. However, this conversion is less obvious when the entry-point function takes an array of structures, because code generation does not support arrays of classes. To work around this limitation of code generation, you can define a class that contains all of the structure objects as a finite number of n-length arrays.
For example, assume an array of patient structures, where each element
contains three fields: id
, a scalar double;
billing
, a scalar double; and test
, a
3x3 array of doubles. Assume two functions that operate on this
patients
array, one that computes the average billing
across the objects in the patients
array, and one that adds a
new patient to the end of the patients
array:
function out = billAvgS(patients) arguments patients (1,:) struct end out = mean([patients.billing]); end function patients = addPatientS(patients, id, billing, test) arguments patients (1,:) struct id (1,1) double billing (1,1) double test (3,3) double end % Create a new patient and add to the end of the patients array newPatient = struct('id', id, 'billing', billing, 'test', test); patients(end+1) = newPatient; end
PatientsClass
that will store
all the data in the
patients
array:classdef PatientsClass properties ids (1,:) double bills (1,:) double tests (3,3,:) double end end
PatientsClass
contains two n-length arrays, ids
and
bills
, and one 3x3 array with n
slices. Thus, the first patient is represented by ids(1)
,
bills(1)
, tests(:,:,1)
; the second
patient is represented by ids(2)
,
bills(2)
, tests(:,:,2)
; and the
nth patient is represented by ids(n)
,
bills(n)
, tests(:,:,n)
. You can then
convert the patients
array into an instance of
patientsClass
:p = PatientsClass; p.ids = [patients.id]; p.bills = [patients.billing]; p.tests = cat(3,patients.test);
billAvgC
and addPatientC
to take an
instance of PatientsClass
instead of the
patients
struct
array:function out = billAvgC(obj) arguments obj (1,1) PatientsClass end %Compute the average billing amount out = mean(obj.bills); end function obj = addPatientC(obj, id, billing, test) arguments obj (1,1) PatientsClass id (1,1) double billing (1,1) double test (3,3) double end % Append new patient data to existing properties obj.ids = [obj.ids, id]; obj.bills = [obj.bills, billing]; obj.tests = cat(3, obj.tests, test); end
billAvgC
and
addPatientC
without further specifying input
types.