Non-constant expression or empty matrix. This expression must be constant because its value determines the size or class of some expression.

How should I correctly pass in variable structure fields to support C/C++ code generation? My problem is simplified by the fact that the field name "str" of the following structure is dynamically changing and the exact value can only be determined at runtime
function out = testStruct()%#codegen
s = struct();
for i = 1:3
str = randGenChar();
s.(str) = i;
end
out = s;
end
function str = randGenChar()
basechar = 'abcdefghijklmnopqrstuvwxyz';
idx = randi(numel(basechar));
str = basechar(1:idx);
end
Then execute command line:
codegen -config:mex testStruct.m -report
Non-constant expression or empty matrix. This expression must be constant
because its value determines the size or class of some expression.
Error in ==> testStruct Line: 6 Column: 8
Code generation failed: To view the report, open('codegen/mex/testStruct/html/report.mldatx')

 Respuesta aceptada

You don't. Code generation cannot handle dynamically generated field names (except perhaps some that can be somehow resolved at compile time through analysis.)
In C and C++ the only form of dynamic field name access to a struct is either by reinterpret-cast and indexing to the appropriate offset, or else by union against a type that you can index.
Once compiled, in C and C++ the only record of the correspondance between struct field name and offset relative to the beginning of the struct, is held in the debug tables. It is not even held for linkage purposes across multiple source files: in C and C++ you commonly #include header files that define the struct fields each time, and if two different source code files define the struct differently then you are dealing with Undefined Behaviour, and are lucky if the compiler happens to notice.
In C especially, everything is compile-time type checking; after that all that gets passed around is constants and pointers, and the compiled function just knows "under this circumstance access a double at offset 72 relative to the pointer that was passed in".

4 comentarios

+1 to this explanation. Coder needs to be able to determine the field name at codegen time. You can sanity check if this is possible by wrapping the name computation in a call to coder.const which forces constant-folding and gives errors if that's not possible
function out = testStruct()%#codegen
s = struct();
for i = 1:3
str = coder.const(randGenChar());
s.(str) = i;
end
out = s;
end
In this case, the call to randi in randGenChar is going to cause you issues with constant folding. You can generate struct field names but only when Coder can compute those names at codegen time. Leveraging coder.const will help you sanity check if your field name computation is constant foldable or not.
As an example, the following code will generate code and produce a struct with fields someField1, someField2, someField3
function out = testStruct()%#codegen
s = struct();
% Unroll the loop to allow i to be a constant
coder.unroll();
for i = 1:3
str = "someField" + i;
s.(str) = i;
end
out = s;
end
You can also do things like
function out = testStruct(inStruct)%#codegen
fields = fieldnames(inStruct);
% Unroll the loop to allow i to be a constant
coder.unroll();
for i = 1:numel(fields)
field = fields{i};
if field ~= "toFilter"
s.(field) = inStruct.(field);
end
end
out = s;
end
>> codegen testStruct -args {struct('toFilter',1,'other',"fff")}
Code generation successful.
>> testStruct_mex(struct('toFilter',1,'other',"fff"))
ans =
struct with fields:
other: "fff"
@Ryan Livingston Thank you for your solution, after trying all 3 of your methods above, unfortunately, it still does not support C/C++ code generation. The reason is as follows:
The randGenChar() function is a simulation to simplify the character vector generated by the dynamic process in my actual program. The above example I have simplified my problem with the randi built-in function and cannot simply use coder.const to force it to change to a constant.
Method2 uses coder.unroll() to expand the for loop, but in practice my code here has multiple layers of for loops and complex logical judgement statements, each layer of the loop will be associated with the previous one, plus the loop index to constant has nothing to do with the field names of the structure I want to set, so coder.unroll is not suitable.
Method3 is indirectly passed to the str field name by way of the input parameter specified as struct, but essentially it still shows that the str field name is obviously specified by manually, and the field name of the input struct is not easily determined, which is actually still a dynamic problem, and there is no substantial solution to the problem.
----------- on the above issue I will do a brief elaboration to see how to correctly solve ---------------------
This time, current function testStruct2 is used to output a structure from the text file "fieldNames.txt" (see bellow) in the specified field names, each line of this text string for a structure field name, the number of lines (the number of structure field names) and the name are not determined, how to write MATLAB code in order to support C/C++ code generation?
The following code is
function out = testStruct2(filename)%#codegen
% codegen command:
% inFile = coder.Constant('fieldNames.txt'); % ok,But the next time the field name specified in this file changes, it can't be read
% inFile = coder.typeof('fieldNames.txt'); % error:Expression could not be reduced to a constant.
% codegen -config:mex testStruct2 -args {inFile}
%
s = struct();
coder.const(filename);
fieldNames = coder.const(@feval,'getFieldNames',filename);
% fieldNames =coder.const(@getFieldNames,filename);% Error: Expression could not be reduced to a constant.
for i = 1:numel(fieldNames)
str = fieldNames{i};
s.(str) = i;
end
out = s;
end
function str = getFieldNames(fieldsFileName)%#codegen
fid = fopen(fieldsFileName);
if fid == -1
error("can't open file\n");
end
fileCloser = onCleanup(@()(fclose(fid)));
% read field name line by line
str = cell(1,0);
while ~feof(fid)
tline = fgetl(fid);
str{end+1} = tline;
end
end
My text file "fieldNames.txt" contains the following contents:
fieldname1
fieldname2
fieldname3
Subsequently, generate the mex by executing the following at the command line:
inFile = coder.Constant('fieldNames.txt');
codegen -config:mex testStruct2 -args {inFile}
Code generation successful.
Then out = testStruct2_mex('fieldNames.txt') always outputs three values(fieldname1,fieldname2,fieldname3), even if I add more field names to the text file 'fieldNames.txt'.
The problem with the above code is that once I specify the filename via coder.const, I can successfully generate the C/C++ code, but the reality is that the content of the filename file is often indeterminate in terms of field names, and there may be more field or new names, which still doesn't actually solve my problem.
It's ok if coder doesn't support this dynamic syntax, but there must to be an alternative way to address such a need.
Any workaround suggestions? @Walter Roberson@Ryan Livingston,thanks again.
Coder unfortunately doesn't support the level of flexibility you need as the number, names, etc. of struct fields must be determined when generating code. Given that dictionary doesn't support codegen, your best bet would be to choose a different data structure to represent things in the generated code. Keeping 2 cell arrays could make sense
fields = {'field1', 'field2', 'field3'};
data = {value1, value2, value3};
When reading from that, you'd need to do a search through fields to find the corresponding index. Depending on the size of your problem, you then might consider keeping fields in sorted order and doing a binary search to make lookup more efficient.
Thanks for the suggestion, hopefully some future version will enhance this support, less user effort to rewrite the code to suit, like end+1 is slowly starting to be supported.

Iniciar sesión para comentar.

Más respuestas (0)

Productos

Versión

R2023a

Etiquetas

Preguntada:

el 18 de Abr. de 2023

Comentada:

el 19 de Abr. de 2023

Community Treasure Hunt

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

Start Hunting!

Translated by