Main Content

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 or cell 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 Argument and Property 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 Properties 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
However, the types and sizes of the fields contained within the 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
You can then rewrite the 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
You can generate code for 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
A single instance of 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);
You can then rewrite 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
You can generate code for 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
To generate C/C++ code for these functions, first construct a 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
A single instance of 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);
Finally, you can rewrite 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
You can generate code for billAvgC and addPatientC without further specifying input types.

See Also

| |

Related Topics