如何确保在生成的 C 代码中正确分配 emxArray?
How can I ensure an emxArray is correctly allocated in generated C code?
我有以下 .m 文件(名为 testmemoryallocation.m
),用于在 Matlab Coder 中生成代码。当然,这只是一个演示概念的测试文件。
function output = testmemoryallocation(dim) %#codegen
%TESTMEMORYALLOCATION Tests allocation of large 3D arrays
output = coder.nullcopy(zeros([dim, dim, dim]));
output(:) = 5;
end
我使用以下构建脚本构建它(来自 Coder 应用程序的默认设置)
%% Create configuration object of class 'coder.CodeConfig'.
cfg = coder.config('lib','ecoder',false);
cfg.GenerateReport = true;
cfg.GenCodeOnly = true;
cfg.HardwareImplementation = coder.HardwareImplementation;
cfg.HardwareImplementation.ProdIntDivRoundTo = 'Undefined';
cfg.HardwareImplementation.TargetIntDivRoundTo = 'Undefined';
%% Define argument types for entry-point 'testmemoryallocation'.
ARGS = cell(1,1);
ARGS{1} = cell(1,1);
ARGS{1}{1} = coder.typeof(0);
%% Invoke MATLAB Coder.
codegen -config cfg testmemoryallocation -args ARGS{1}
此构建过程生成的 C 代码如下所示:
/*
* testmemoryallocation.c
*
* Code generation for function 'testmemoryallocation'
*
*/
/* Include files */
#include "rt_nonfinite.h"
#include "testmemoryallocation.h"
#include "testmemoryallocation_emxutil.h"
/* Function Definitions */
void testmemoryallocation(double dim, emxArray_real_T *output)
{
int i0;
int loop_ub;
/* TESTMEMORYALLOCATION Tests allocation of large 3D arrays */
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[0] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[1] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[2] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
loop_ub = (int)dim * (int)dim * (int)dim;
for (i0 = 0; i0 < loop_ub; i0++) {
output->data[i0] = 5.0;
}
}
/* End of code generation (testmemoryallocation.c) */
问题是:MATLAB Coder(在 testmemoryallocation.m
文件中)是否有办法验证内存是否已实际分配?内存分配发生在 emxEnsureCapacity 函数中,它本身没有 return 有关成功分配的信息(据我所知)。如果没有,我希望能够从调用函数中优雅地退出'没有足够的系统内存来完成该过程。例如,我想向 testmemoryallocation 添加一个 result
输出,指示分配内存时是否发生错误。有办法吗?
实际上,问题归结为:是否有一种方法可以访问 emxArrays 以测试结构的 data
字段不为空。也许是用 coder.ceval 调用的 .c 文件?有没有办法指示 coder.ceval 传递 emxArray 类型而不是基本类型(double 或 int),以便可以编写底层 .c 代码来与结构交互?
编辑:
接受的优秀答案解决了这个问题和生成的 c 代码中 (2015a) Coder 中的潜在错误。在 _emxutil 文件中,如果请求的元素数量超过 intmax/2
,emxEnsureCapacity 函数可能会出现无限循环。
我已经解决了这个问题,我认为我有一个很好的解决方案。我创建了一个名为 isallocated
的 MATLAB 函数,它将可变大小的双精度浮点数组作为输入,如果数组分配正确则输出 1,否则输出 0。如果在MATLAB环境下调用,总是returns 1.
function result = isallocated(inarray) %#codegen
%ISALLOCATED Returns true if the array memory is allocated properly.
% Will check the array to verify that the data pointer is not null.
if coder.target('MATLAB')
result = int32(1);
return;
end
result = int32(0);
coder.cinclude('emxArray_helpers.h');
coder.updateBuildInfo('addSourceFiles', 'emxArray_helpers.c');
result = coder.ceval('isallocated_helper', coder.ref(inarray));
end
这个函数只是isallocated_helper
的包装器,这里唯一的技巧是coder.ref
命令将指针提取到inarray中的数据数组。这具有获取 emxArray 结构的 data
成员值的效果。
c 函数 isallocated_helper
出奇的无聊;这只是一个测试,看指针是否为空。为了完整起见,我将代码放在这里:
// emxArray_helpers.c
// A set of helper functions for emxArray types
int isallocated_helper(double * arrayptr)
{
if (arrayptr)
return 1;
return 0;
}
仅此而已。这提供了一个很好的测试,如果我更改 testmemoryallocation.m
文件来使用它,它看起来像:
function [result, output] = testmemoryallocation(dim) %#codegen
%TESTMEMORYALLOCATION Tests allocation of large 3D arrays
output = coder.nullcopy(zeros([dim, dim, dim]));
result = isallocated(output);
if result == 1
output(:) = 5;
else
output = zeros([1,1,2]);
end
end
在生成的 c 代码片段中,我们可以看到测试:
...
/* TESTMEMORYALLOCATION Tests allocation of large 3D arrays */
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[0] = (int)dim;
output->size[1] = (int)dim;
output->size[2] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
/* ISALLOCATED Returns true if the array memory is allocated properly. */
/* Will check the array to verify that the data pointer is not null. */
*result = isallocated_helper(&output->data[0]);
if (*result == 1) {
...
很高兴看到你在进步。关于 emxEnsureCapacity 问题,这是不幸的,它是一个错误。我会处理它以确保我们在即将发布的版本中修复它。同时,有一种方法可以修补生成的源代码。对于配置对象,在生成 C 代码后有 'PostCodeGenCommand' 选项,即 executed/evaluated。这允许您在编译之前应用补丁。例如,
cfg.PostCodeGenCommand = 'mypatch';
然后'mypatch.m'是:
function mypatch
cfiles = all_cfiles([pwd filesep 'codegen']);
for i = 1:numel(cfiles)
cfile = cfiles{i};
if strcmp(cfile(end-9:end), '_emxutil.c')
text = fileread(cfile);
text = regexprep(text, '(while \(i < newNumel\) \{\s+i <<= 1; (\s+\})', ' if (i < 0) i = 0x7fffffff; ');
filewrite(cfile, text);
disp(cfiles{i});
end
end
function filewrite(filename, text)
f = fopen(filename, 'w');
fprintf(f, '%s', text);
fclose(f);
function files = all_cfiles(d)
files = {};
fs = dir(d);
for i = 1:numel(fs)
if (fs(i).name(1) == '.')
continue
end
if fs(i).isdir
files = [files all_cfiles([d filesep fs(i).name])];
else
if strcmp(fs(i).name(end-1:end), '.c')
files{end+1} = [d filesep fs(i).name];
end
end
end
关于将 'emxArray' 传递给外部 C 函数(在 coder.ceval() 调用中使用 coder.ref())这有点问题。这是因为 'emxArray' 表示可能存在也可能不存在,具体取决于矩阵的大小。还有一个阈值(在配置对象中)可以让您知道何时切换到 "full" emxArrays 或将它们保留为 "upperbound allocated" 变量(调用边界被拆分为数据和大小作为单独的变量。 ) 可变大小数组有更多的表示形式(例如,如果结构中有上界变量。)这是我们没有 emxArrays 直接接口的主要原因,因为类型兼容性可能会根据这些参数发生变化。因此,始终提取数据将使其类型兼容。如果你需要访问大小,这里假设 'x' 是一个向量,那么你可以显式地传递大小:
coder.ceval('foo', x, int32(numel(x)));
不幸的是,这不允许您更改 'foo' 中 'x' 的大小。
还有一件事你可以做,但请直接与我联系(电子邮件),我会告诉你更多。
亚历山大·波蒂玛
软件工程师
MATLAB 程序员团队
数学作业
我有以下 .m 文件(名为 testmemoryallocation.m
),用于在 Matlab Coder 中生成代码。当然,这只是一个演示概念的测试文件。
function output = testmemoryallocation(dim) %#codegen
%TESTMEMORYALLOCATION Tests allocation of large 3D arrays
output = coder.nullcopy(zeros([dim, dim, dim]));
output(:) = 5;
end
我使用以下构建脚本构建它(来自 Coder 应用程序的默认设置)
%% Create configuration object of class 'coder.CodeConfig'.
cfg = coder.config('lib','ecoder',false);
cfg.GenerateReport = true;
cfg.GenCodeOnly = true;
cfg.HardwareImplementation = coder.HardwareImplementation;
cfg.HardwareImplementation.ProdIntDivRoundTo = 'Undefined';
cfg.HardwareImplementation.TargetIntDivRoundTo = 'Undefined';
%% Define argument types for entry-point 'testmemoryallocation'.
ARGS = cell(1,1);
ARGS{1} = cell(1,1);
ARGS{1}{1} = coder.typeof(0);
%% Invoke MATLAB Coder.
codegen -config cfg testmemoryallocation -args ARGS{1}
此构建过程生成的 C 代码如下所示:
/*
* testmemoryallocation.c
*
* Code generation for function 'testmemoryallocation'
*
*/
/* Include files */
#include "rt_nonfinite.h"
#include "testmemoryallocation.h"
#include "testmemoryallocation_emxutil.h"
/* Function Definitions */
void testmemoryallocation(double dim, emxArray_real_T *output)
{
int i0;
int loop_ub;
/* TESTMEMORYALLOCATION Tests allocation of large 3D arrays */
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[0] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[1] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[2] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
loop_ub = (int)dim * (int)dim * (int)dim;
for (i0 = 0; i0 < loop_ub; i0++) {
output->data[i0] = 5.0;
}
}
/* End of code generation (testmemoryallocation.c) */
问题是:MATLAB Coder(在 testmemoryallocation.m
文件中)是否有办法验证内存是否已实际分配?内存分配发生在 emxEnsureCapacity 函数中,它本身没有 return 有关成功分配的信息(据我所知)。如果没有,我希望能够从调用函数中优雅地退出'没有足够的系统内存来完成该过程。例如,我想向 testmemoryallocation 添加一个 result
输出,指示分配内存时是否发生错误。有办法吗?
实际上,问题归结为:是否有一种方法可以访问 emxArrays 以测试结构的 data
字段不为空。也许是用 coder.ceval 调用的 .c 文件?有没有办法指示 coder.ceval 传递 emxArray 类型而不是基本类型(double 或 int),以便可以编写底层 .c 代码来与结构交互?
编辑:
接受的优秀答案解决了这个问题和生成的 c 代码中 (2015a) Coder 中的潜在错误。在 _emxutil 文件中,如果请求的元素数量超过 intmax/2
,emxEnsureCapacity 函数可能会出现无限循环。
我已经解决了这个问题,我认为我有一个很好的解决方案。我创建了一个名为 isallocated
的 MATLAB 函数,它将可变大小的双精度浮点数组作为输入,如果数组分配正确则输出 1,否则输出 0。如果在MATLAB环境下调用,总是returns 1.
function result = isallocated(inarray) %#codegen
%ISALLOCATED Returns true if the array memory is allocated properly.
% Will check the array to verify that the data pointer is not null.
if coder.target('MATLAB')
result = int32(1);
return;
end
result = int32(0);
coder.cinclude('emxArray_helpers.h');
coder.updateBuildInfo('addSourceFiles', 'emxArray_helpers.c');
result = coder.ceval('isallocated_helper', coder.ref(inarray));
end
这个函数只是isallocated_helper
的包装器,这里唯一的技巧是coder.ref
命令将指针提取到inarray中的数据数组。这具有获取 emxArray 结构的 data
成员值的效果。
c 函数 isallocated_helper
出奇的无聊;这只是一个测试,看指针是否为空。为了完整起见,我将代码放在这里:
// emxArray_helpers.c
// A set of helper functions for emxArray types
int isallocated_helper(double * arrayptr)
{
if (arrayptr)
return 1;
return 0;
}
仅此而已。这提供了一个很好的测试,如果我更改 testmemoryallocation.m
文件来使用它,它看起来像:
function [result, output] = testmemoryallocation(dim) %#codegen
%TESTMEMORYALLOCATION Tests allocation of large 3D arrays
output = coder.nullcopy(zeros([dim, dim, dim]));
result = isallocated(output);
if result == 1
output(:) = 5;
else
output = zeros([1,1,2]);
end
end
在生成的 c 代码片段中,我们可以看到测试:
...
/* TESTMEMORYALLOCATION Tests allocation of large 3D arrays */
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[0] = (int)dim;
output->size[1] = (int)dim;
output->size[2] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
/* ISALLOCATED Returns true if the array memory is allocated properly. */
/* Will check the array to verify that the data pointer is not null. */
*result = isallocated_helper(&output->data[0]);
if (*result == 1) {
...
很高兴看到你在进步。关于 emxEnsureCapacity 问题,这是不幸的,它是一个错误。我会处理它以确保我们在即将发布的版本中修复它。同时,有一种方法可以修补生成的源代码。对于配置对象,在生成 C 代码后有 'PostCodeGenCommand' 选项,即 executed/evaluated。这允许您在编译之前应用补丁。例如,
cfg.PostCodeGenCommand = 'mypatch';
然后'mypatch.m'是:
function mypatch
cfiles = all_cfiles([pwd filesep 'codegen']);
for i = 1:numel(cfiles)
cfile = cfiles{i};
if strcmp(cfile(end-9:end), '_emxutil.c')
text = fileread(cfile);
text = regexprep(text, '(while \(i < newNumel\) \{\s+i <<= 1; (\s+\})', ' if (i < 0) i = 0x7fffffff; ');
filewrite(cfile, text);
disp(cfiles{i});
end
end
function filewrite(filename, text)
f = fopen(filename, 'w');
fprintf(f, '%s', text);
fclose(f);
function files = all_cfiles(d)
files = {};
fs = dir(d);
for i = 1:numel(fs)
if (fs(i).name(1) == '.')
continue
end
if fs(i).isdir
files = [files all_cfiles([d filesep fs(i).name])];
else
if strcmp(fs(i).name(end-1:end), '.c')
files{end+1} = [d filesep fs(i).name];
end
end
end
关于将 'emxArray' 传递给外部 C 函数(在 coder.ceval() 调用中使用 coder.ref())这有点问题。这是因为 'emxArray' 表示可能存在也可能不存在,具体取决于矩阵的大小。还有一个阈值(在配置对象中)可以让您知道何时切换到 "full" emxArrays 或将它们保留为 "upperbound allocated" 变量(调用边界被拆分为数据和大小作为单独的变量。 ) 可变大小数组有更多的表示形式(例如,如果结构中有上界变量。)这是我们没有 emxArrays 直接接口的主要原因,因为类型兼容性可能会根据这些参数发生变化。因此,始终提取数据将使其类型兼容。如果你需要访问大小,这里假设 'x' 是一个向量,那么你可以显式地传递大小:
coder.ceval('foo', x, int32(numel(x)));
不幸的是,这不允许您更改 'foo' 中 'x' 的大小。
还有一件事你可以做,但请直接与我联系(电子邮件),我会告诉你更多。
亚历山大·波蒂玛
软件工程师
MATLAB 程序员团队
数学作业