Main Content

从生成的代码中调用自定义 C/C++ 代码

在 MATLAB® 代码中,您可以直接调用外部 C/C++ 代码,也称为自定义代码或原有代码。要调用 C/C++ 函数,请使用 coder.ceval。代码生成器将您的 C/C++ 代码集成到从 MATLAB 生成的 C/C++ 代码中。当您要将生成的代码与外部库、优化的代码或在 C/C++ 中开发的目标文件结合使用时,请集成代码。当外部代码使用 MATLAB 未定义或不能识别的变量类型时,请将 coder.opaque 函数与 coder.ceval 结合使用。要保留某些标识符名称,以便在要与生成的代码集成的自定义 C/C++ 代码中使用,请使用 coder.reservedName 函数。

以下是外部代码集成的一些主要工作流。有关更多示例,请参阅 coder.ceval 参考页。

注意

使用 coder.ceval,您可以不受限制地访问外部代码。在代码中误用这些函数或者代码中的错误可能会导致 MATLAB 不稳定并停止工作。要调试代码并分析编译中的错误消息,请查看代码生成报告中的编译日志选项卡。

coder.ceval 只能在 MATLAB 代码中用于代码生成。在未编译的 MATLAB 代码中,coder.ceval 将生成错误。要确定 MATLAB 函数是否在 MATLAB 中执行,请使用 coder.target。如果函数在 MATLAB 中执行,请调用 MATLAB 版本的 C/C++ 函数。

调用 C 代码

此示例说明如何使用 coder.ceval 将简单的 C 函数与 MATLAB® 代码集成。以 MATLAB 函数 mathOps 为例:

function [added, multed] = mathOps(in1, in2)
added = in1+in2;
multed = in1*in2; 
end

对于此示例,假设您要使用外部 C 代码实现加法运算。请考虑在 adder.c 文件中实现的 C 函数 adder

#include <stdio.h>
#include <stdlib.h>
#include "adder.h"

double adder(double in1, double in2)
{
  return in1 + in2;
}

要将 adder 与 MATLAB 代码集成,需要具有包含函数原型的头文件。请参阅文件 adder.h

double adder(double in1, double in2);

使用 coder.ceval 命令在 mathOpsIntegrated.m 中调用 C 函数。使用 coder.cinclude 包含头文件。

function [added, multed] = mathOpsIntegrated(in1, in2)
%#codegen
% for code generation, preinitialize the output variable
% data type, size, and complexity 
added = 0;
% generate an include in the C code
coder.cinclude('adder.h');
% evaluate the C function
added = coder.ceval('adder', in1, in2); 
multed = in1*in2; 
end

要生成代码,请使用 codegen 命令。将源文件 adder.c 指定为输入。要测试 C 代码,请执行 MEX 函数并检查输出结果。

codegen mathOpsIntegrated -args {1, 2} adder.c

[test1, test2] = mathOpsIntegrated_mex(10, 20)
Code generation successful.


test1 =

    30


test2 =

   200

从一个 C 函数返回多个值

C 语言限制函数返回多个输出。函数只能返回单个标量值。您可以使用 MATLAB 函数 coder.refcoder.rrefcoder.wref 从一个外部 C/C++ 函数返回多个输出。

例如,假设您编写 MATLAB 函数 foo,该函数接受 xy 两个输入并返回 abc 三个输出。在 MATLAB 中,您可以按如下方式调用此函数:

[a,b,c] = foo(x,y)

如果将 foo 重写为 C 函数,则不能通过一个 return 语句返回三个单独的值 abc。在这种情况下,请创建一个具有多个指针类型参量的 C 函数,并按引用传递输出参数。例如:

void foo(double x,double y,double *a,double *b,double *c)

然后,您可以使用 coder.ceval 函数从 MATLAB 函数调用该 C 函数。

coder.ceval('foo',x,y,coder.ref(a),coder.ref(b),coder.ref(c));

如果您的外部 C 函数仅按引用方式读写内存,则可以使用 coder.wrefcoder.rref 函数而不是 coder.ref。在某些情况下,这些函数可以进一步优化生成的代码。当您使用 coder.wref(arg) 按引用传递 arg 时,外部 C/C++ 函数必须对 arg 引用的内存进行完全初始化。

按引用传递数据

此示例说明如何按引用向外部 C 函数传递数据或传递来自外部 C 函数的数据。

按引用传递是 C/C++ 代码集成的一种重要方法。按引用传递数据时,程序不需要将数据从一个函数复制到另一个函数。按值传递时,C 代码只能返回单个标量变量。按引用传递时,C 代码可以返回多个变量,包括数组。

以 MATLAB 函数 adderRef 为例。此函数使用外部 C 代码将两个数组相加。coder.rrefcoder.wref 命令指示代码生成器将指针传递给数组,而不是复制它们。

function out = adderRef(in1, in2)
%#codegen
out = zeros(size(in1)); 
% the input numel(in1) is converted to integer type 
% to match the cAdd function signature
coder.ceval('cAdd', coder.rref(in1), coder.rref(in2), coder.wref(out), int32(numel(in1)) );
end

C 代码 cAdd.c 使用线性索引来访问数组的元素:

#include <stdio.h>
#include <stdlib.h>
#include "cAdd.h"  

void cAdd(const double* in1, const double* in2, double* out, int numel)
{
    int i;
    for (i=0; i<numel; i++) {
        out[i] = in1[i] + in2[i];
    }
}

要编译 C 代码,您必须提供带有函数签名的头文件 cAdd.h

void cAdd(const double* in1, const double* in2, double* out, int numel);

通过生成 MEX 函数并将其输出与 MATLAB 中的加法运算的输出进行比较来测试 C 代码。

A = rand(2,2)+1;
B = rand(2,2)+10;

codegen adderRef -args {A, B} cAdd.c cAdd.h -report

if (adderRef_mex(A,B) - (A+B) == 0)
    fprintf(['\n' 'adderRef was successful.']);
end
Code generation successful: To view the report, open('codegen/mex/adderRef/html/report.mldatx')


adderRef was successful.

集成使用自定义数据类型的外部代码

此示例说明如何调用使用了非 MATLAB® 定义的原生数据类型的 C 函数。

例如,如果您的 C 代码对 C 'FILE *' 类型执行文件输入或输出,则 MATLAB 中没有对应的类型。要在 MATLAB 代码中与此数据类型进行交互,必须使用 coder.opaque 函数对其进行初始化。对于结构体类型,可以使用 coder.cstructname

例如,假设有 MATLAB 函数 addCTypes.m。此函数使用其输入类型在外部代码中定义的 coder.ceval。函数 coder.opaque 在 MATLAB 中对该类型进行初始化。

function [out] = addCTypes(a,b)
%#codegen
% generate include statements for header files
coder.cinclude('MyStruct.h');
coder.cinclude('createStruct.h');
coder.cinclude('useStruct.h');
% initialize variables before use
in = coder.opaque('MyStruct');
out = 0;
% call C functions
in = coder.ceval('createStruct',a,b);
out = coder.ceval('useStruct',in); 
end


createStruct 函数输出 C 结构体类型:

#include <stdio.h>
#include <stdlib.h>
#include "MyStruct.h"
#include "createStruct.h"

struct MyStruct createStruct(double a, double b) {
    struct MyStruct out;
    out.p1 = a;
    out.p2 = b;
    return out;
}

useStruct 函数对该 C 类型执行操作:

#include "MyStruct.h"
#include "useStruct.h"

double useStruct(struct MyStruct in) {
    return in.p1 + in.p2;
}

要生成代码,请将源 (.c) 文件指定为输入:

codegen addCTypes -args {1,2} -report createStruct.c useStruct.c
Code generation successful: To view the report, open('codegen/mex/addCTypes/html/report.mldatx')

集成使用指针、结构体和数组的外部代码

此示例说明如何将对 C 样式数组执行运算的外部代码与 MATLAB® 代码集成。外部代码计算数组数据的和。您可以自定义代码以更改输入数据或计算。

此示例说明如何合并外部代码集成功能的多个不同元素。例如,您可以:

  • 使用 coder.cstructname 对接外部结构体类型

  • 使用 coder.opaque 对接外部指针类型

  • 使用 coder.ceval 执行外部代码

  • 使用 coder.ref 按引用将数据传递到外部代码

浏览集成后的代码

extSum 函数使用外部 C 代码对 32 位整数数组执行求和运算。数组大小由用户输入控制。

function x = extSum(u)
%#codegen
% set bounds on input type to use static memory allocation
u = int32(u);
assert(0 < u && u < 101);
% initialize an array
temparray = int32(1):u;
% declare an external structure and use it
s = makeStruct(u);
x = callExtCode(s, temparray);


为了简化生成的代码,请对数组大小设置限制。限制可防止在生成的代码中使用动态内存分配。

函数 makeStruct 声明一种 MATLAB 结构体类型,并使用 coder.opaque 将其中一个字段初始化为指针类型。与此定义对应的 C 结构体包含在一个头文件中,您通过使用 coder.cstructname 函数中的 HeaderFile 参数提供该头文件。C 结构体类型提供了整数数组的简单表示。

function s = makeStruct(u)
% create structure type based on external header definition
s.numel = u;
s.vals = coder.opaque('int32_T *','NULL');
coder.cstructname(s,'myArrayType','extern','HeaderFile','arrayCode.h');

在完全初始化外部结构体类型后,您可以将其作为输入传递给 callExtCode 函数中的外部代码。此函数对数组进行初始化,调用对数组执行的运算以返回单个输出,然后释放经过初始化的内存。

function x = callExtCode(s, temparray)
% declare output type
x = int32(0);
% declare external source file
coder.updateBuildInfo('addSourceFiles','arrayCode.c');
% call c code
coder.ceval('arrayInit',coder.ref(s),coder.ref(temparray));
x = coder.ceval('arraySum',coder.ref(s));
coder.ceval('arrayDest',coder.ref(s));

该函数使用 coder.updateBuildInfo 将 .c 文件提供给代码生成器。

生成 MEX 函数

要生成可以在 MATLAB 中运行和测试的 MEX 函数,请输入:

codegen extSum -args {10}
Code generation successful.

测试 MEX 函数。输入:

extSum_mex(10)
ans =

  int32

   55

包含在文件 arrayCode.carrayCode.h 中的外部 C 代码使用自定义类型定义 int32_T。生成的 MEX 代码将生成并使用此自定义类型定义。如果要生成使用此自定义数据类型的独立(lib、dll 或 exe)代码,您可以修改配置对象的 DataTypeReplacement 属性。请参阅Mapping MATLAB Types to Types in Generated Code

另请参阅

| | | | | | | |

相关主题