在生成的函数接口中使用C数组

在大多数情况下,当您为MATLAB生成代码时®函数接受或返回数组时,生成的C/ c++函数接口包含数组。要使用生成的函数接口,请了解如何定义和构造生成的C/ c++数组。尤其要学会使用emxArray生成以表示动态分配的数组的数据结构。

在生成C/ c++代码时,会创建一个示例主文件,演示如何在生成的函数代码中使用数组。您可以使用示例main作为自己应用程序的模板或起点。

在生成的C/ c++代码中实现数组

代码生成器生成C/ c++数组定义,这些定义取决于数组元素类型以及数组使用静态还是动态内存分配。数组的两种内存分配需要两种不同的实现:

  • 对于大小限制在预定义阈值内的数组,生成的C/ c++定义由一个指向内存的指针和一个存储数组元素总数(数组大小)的整数组成。这个数组的内存来自程序堆栈,是静态分配的。

  • 对于在编译时大小未知且未绑定的数组,或其绑定超过预定义的阈值的数组,生成的C/ c++定义由一个称为emxArray.当一个emxArray时,根据当前数组大小设置中间存储边界。在程序执行期间,当超过中间存储边界时,生成的代码从堆中占用额外的内存空间,并将其添加到emxArray存储。这个数组的内存是动态分配的。

默认情况下,在阈值大小范围内的数组不会在生成的代码中使用动态分配。您也可以禁用动态内存分配,并更改动态内存分配阈值。看到控制可变大小数组的内存分配

这个表列出了生成代码中数组表示的一些典型情况。

算法描述和数组大小

MATLAB函数

生成C函数接口

将它们放置到固定大小的1 × 500行向量上。

固定大小,阈值范围内。

函数B = create_vec0% # codegenB = 0 (1500);j = 1;i = 1:50 0如果round(rand) B(1,j) = 1;J = J + 1;结束结束
空白create_vec0(双B [500])

将1推入到以300个元素为界的可变大小行向量上。

可变大小,阈值范围内。

函数B = create_vec% # codegenB = 0 (1,0);coder.varsize (“B”[300], [0 1]);i = 1:50 0如果round(rand) B = [1 B];结束结束
void create_vec(double B_data[], int B_size[2])

将1推入到一个以30,000个元素为界的可变大小行向量上。

可变大小,不受阈值限制。

函数B = create_vec2% # codegenB = 0 (1,0);coder.varsize (“B”[30000], [0 1]);i = 1:50 0如果round(rand) B = [1 B];结束结束
空白create_vec2 (emxArray_real_T * B)

创建一个数组,其大小由无界整数输入决定。

在编译时未知且无界。

函数y = create_vec3 (n)% # codegeny = int8 ((1, n));
void create_vec3(int n, emxArray_int8_T *y)

emxArray动态数据结构定义

在生成的C/ c++代码中emxArray数据结构定义取决于它所存储的元素的数据类型。一般定义的形式为:

struct emxArray_ { *data;int *大小;int allocatedSize;int numDimensions;boolean_T canFreeData;};

的定义,<类型>表示数据类型和<名称>用于标识的名称emxArray结构。代码生成器选择<名称>基于为MEX代码生成定义的类型,如中所列将MATLAB类型映射到生成代码中的类型

作为一个例子,考虑emxArray为函数生成的定义create_vec2.的<名称>emxArray_real_T<类型>

struct emxArray_real_T{双*数据;int *大小;int allocatedSize;int numDimensions;boolean_T canFreeData;};

不寻求预测的条目<类型>而且<名称>在代码生成之前。相反,在代码生成完成后,检查文件myFunction < >_types.h来自代码生成报告。myFunction < >是入口点函数的名称。

生成的代码还可以定义emxArray结构用类型定义语句,如在这些例子中。

typedef struct {emxArray_real_T *f1;} cell_wrap_0;类型定义结构{cell_wrap_0 *data;int *大小;int allocatedSize;int numDimensions;boolean_T canFreeData;} emxArray_cell_wrap_0;

该表描述了emxArray结构字段。

描述
<类型> *数据 指向类型元素数组的指针<类型>
int *大小 指向大小向量的指针。size向量的第i个元素存储数组第i维的长度。
int allocatedSize 分配给数组的内存元素的数目。如果数组大小发生变化,生成的代码将根据新的大小重新分配内存。
int numDimensions 大小向量的长度。在不跨越未分配或未使用内存的情况下可以访问的维度数量。
boolean_T canFreeData

指示如何释放内存的布尔标志。只在内部使用emxArray处理例程。

  • 真正的—生成的代码自己释放内存。

  • 实例化的程序emxArray必须手动释放由指向的内存数据

用于交互的效用函数emxArray数据

创造和互动emxArray当您的C/ c++代码中包含数据时,代码生成器将导出一组具有用户友好API的C/ c++助手函数。使用这些函数可以确保正确地初始化和销毁emxArray数据类型。要使用这些函数,请为生成的头文件插入include语句myFunction < >_emxAPI.h在你的C代码中。myFunction < >是入口点函数的名称。操作的代码生成器产生的其他函数emxArray中定义的数据,myFunction < >_emxutil.h,不适于手动使用。

的默认生成的示例主文件自由dll,exe代码包括对emxArrayAPI函数。示例主代码初始化emxArray数据为泛型零值。要使用实际的数据输入和值,请修改示例主文件或创建自己的主文件。有关使用main函数的更多信息,请参见使用主函数示例合并生成的代码

该表显示了导出的列表emxArrayAPI函数。的初始行数、列数或维数,有些API函数接受emxArray数据。每个维度都可以根据需要增长以容纳新数据。

emxArrayHelper函数 描述

emxArray_ *emxCreate_(int rows, int cols)

创建指向二维的指针emxArray,其中数据元素初始化为零。为数据分配新内存。

emxArray_ *emxCreateND_(int numDimensions, int *size)

创建指向n维的指针emxArray,其中数据元素初始化为零。为数据分配新内存。

emxArray_ *emxCreateWrapper_( *data, int rows, int cols)

创建指向二维的指针emxArray.使用您提供的数据和内存,并将其封装到emxArray数据结构。集canFreeData防止意外释放用户内存。

emxArray_ *emxCreateWrapperND_( *data, int numDimensions, int *size)

创建指向n维的指针emxArray.使用您提供的数据和内存,并将其封装到emxArray数据结构。集canFreeData防止意外释放用户内存。

(emxArray_ **pEmxArray, int numDimensions)

对象的双指针分配内存emxArray

空白emxDestroyArray_ <名称> (emxArray_ <名称> * emxArray)

对象分配的动态内存emxCreateemxInitArray功能。

代码生成器导出emxArrayAPI函数仅用于作为入口点函数参数的数组或由coder.ceval

例子

使用静态分配数组的函数接口

考虑MATLAB函数myuniquetol为可变大小数据生成代码

函数B = myuniquetol(A, tol)% # codegen一种=(一个);coder.varsize (“B”, [1 100], [0 1]);B = 0 (1,0);k = 1;我= 2:长度(A)如果abs(A(k) - A(i)) > tol B = [B A(i)];k =我;结束结束

生成的代码myuniquetol.使用coder.typeof将输入类型指定为有界的可变大小数组和标量双精度数组。

codegen配置:自由报告myuniquetolarg游戏{编码器。类型of(0,[1 100],[0 1]),coder.typeof(0)}

该声明coder.varsize (“B”,[1 100], [0 1])指定B是一个可变大小的数组,其第一个维度固定为1,第二个维度最多可以变化到100个元素。因为数组的最大大小B在默认阈值大小范围内时,代码生成器为数组使用静态内存分配。

生成的函数接口为:

void myuniquetol(const double A_data[], const int A_size[2], double tol, double B_data[], int B_size[2])

函数接口声明输入参数一个输出参数BA_size包含的大小一个.打完电话后myuniquetolB_size包含的大小B

使用B_size确定…元素的数量B你可以在呼叫后访问myuniquetolB_size [0]包含第一个维度的大小。B_size [1]包含第二个维度的大小。因此,元素的个数BB_size [0] * B_size [1].尽管BOne hundred.元素在C代码中,只有B_size [0] * B_size [1]元素包含有效的数据。

这个C main函数演示了如何调用myuniquetol

void main(){双A[100], B[100];int A_size[2] = {1,100};int B_size [2];int我;For (i = 0;我< 100;i++) {A[i] = (double)1/i;} myuniquetol(A, A_size, 0.1, B, B_size);}

创建一个emxArray通过使用emxCreateemxInitArray功能

emxCreate而且emxCreateNDAPI函数创建一个emxArray,根据需要从堆中分配新内存。然后您可以使用emxArray作为生成代码的输入或输出。这个C代码示例演示了如何使用emxCreate.假设您已经为函数生成了源代码myFunction它使用数据类型emxArray_uint32_T

#include  #include  #include "myFunction_emxAPI.h" #include "myFunction.h" int main(int argc, char *argv[]){/*创建10 × 10 uint32_T emxArray */ emxArray_uint32_T *pEmx = emxCreate_uint32_T(10,10);/*初始化emxArray内存,如果需要*/ int k = 0;For (k = 0;k < 100;++k) {pEmx->data[k] = (uint32_T) k;} /*这里使用pEmx数组;*/ /*插入调用myFunction */ /*释放pEmx中分配的所有内存*/ /* This DOES free pEmx->data */ emxDestroyArray_uint32_T(pEmx);/*未使用的*/ (void)argc;(空白)argv;返回0; }

在本例中,您知道的初始大小emxArray.如果不知道数组的大小,比如在使用数组存储输出时,可以为而且关口字段。例如,如果你不知道列数,你可以这样写:

emxArray_uint32_T *pEmx = emxCreate_uint32_T(10,0);

数据结构会根据需要增长以容纳数据。函数运行后,通过访问大小而且numDimensions字段。

使用emxInitArrayAPI函数创建一个作为输出返回的数组,您事先不知道该数组的大小。例如,创建一个emxArray对于两个尺寸未知的维度,你可以这样写:

emxArray_uint32_T *年代;emxInitArray_uint32_T(谨此告知,2);

将现有数据加载到emxArray

emxCreateWrapper而且emxCreateWrapperNDAPI函数使您能够将现有的内存和数据装入或包装到emxArray将数据传递给生成的函数。这个C代码示例演示了如何使用emxCreateWrapper.假设您已经为函数生成了源代码myFunction它使用数据类型emxArray_uint32_T

#include  #include  #include "myFunction_emxAPI.h" #include "myFunction.h" int main(int argc, char *argv[]){/*创建一个包含uint32_T值的10 × 10 C数组*/ uint32_T x[100];Int k = 0;emxArray_uint32_T *pEmx = NULL;For (k = 0;k < 100;k++) {x[k] = (uint32_T) k;} /*将现有数据加载到emxArray */ pEmx = emxCreateWrapper_uint32_T(x,10,10);/*此处使用pEmx;*/ /*插入调用myFunction */ /*释放pEmx中分配的所有内存*/ /*这不会释放pEmx->数据,因为使用了包装器函数*/ emxDestroyArray_uint32_T(pEmx);/*未使用的*/ (void)argc; (void)argv; return 0; }

创建和使用嵌套emxArray数据

此示例演示如何使用生成的包含emxArray嵌套在其他内部的数据emxArray数据。若要使用生成的代码,请在主函数或调用函数中初始化emxArray数据从最下面的节点向上。

MATLAB算法

这个MATLAB算法遍历一个名为myarray.每个结构都包含较低层的值数组。该算法对每个低级别数组的元素进行排序和求和结构体

% y是形式的结构数组%结构(“值”,[…]),“排序”,[…]], 'sum',…)函数y = processNestedArrays (y)% # codegencoder.cstructname (y,“myarray”);I = 1:numel(y) y(I)。排序=排序(y(我). values);y (i)。金额= (y(我). values)之和;结束

生成MEX函数用于测试

作为第一步,为了能够测试算法,生成MEX函数。使用coder.typeof的无界、可变大小的行向量手动指定输入结构体,它们本身包含无界的、大小可变的行向量。

myarray = coder.typeof (...结构(“值”编码器。类型of(0, [1 inf]),...“排序”编码器。类型of(0, [1 inf]),...“和”, code .typeof(0)), [1 inf]);codegenarg游戏{myarray}processNestedArrays

检查生成的函数接口

MEX函数源代码包含专门的代码,使其能够与MATLAB运行时环境交互,这使其阅读起来更加复杂。要生成更简化的源代码,请生成库代码。

codegen配置:自由arg游戏{myarray}processNestedArrays报告
代码生成成功:要查看报告,请打开('codegen/lib/processNestedArrays/html/report.mldatx')。

检查生成的函数代码processNestedArrays.c来自代码生成报告。生成的示例主文件c方法创建和初始化输入,从而调用生成的函数代码emxCreateAPI函数。

编写并使用您自己的自定义主文件进行初始化emxArray数据

尽管生成的示例主要展示了如何调用生成的函数代码,但它不包含有关所需输入值的信息。使用示例main作为指导,编写您自己的主文件。使用您所选择的编码风格和首选项。指定输入的值,并根据需要插入预处理和后处理代码。

该文件processNestedArrays_main.c显示了一个示例。该主文件使用emxArrayAPI函数来创建和初始化结构数据。对于生成的示例主文件和这个手工编写的主文件,代码都初始化emxArray在底部(叶)节点上的数据,并将该数据分配给上面的节点。

类型processNestedArrays_main.c
#include  #include  #include "processNestedArrays_emxAPI.h" #include " processnestedarrays_h " static void print_vector(emxArray_real_T *v) {int i;printf(“(”);For (i = 0;我< v - >大小[1];i++) {if (I > 0) printf(" ");printf(" %。0 f”,v - >数据[我]);} printf(“\ n”);} int main(int argc, char *argv[]) {int i;静态双值_1[]= {5,3,4,1,2,6}; static double values_2[] = { 50, 30, 40, 10, 20, 60 }; static double values_3[] = { 42, 4711, 1234 }; static double * values[] = { values_1, values_2, values_3 }; static int values_len[] = { 6, 6, 3 }; /* Setup myarray emxArrays */ emxArray_myarray *myarr = emxCreate_myarray(1, 3); /* Create outer array */ for (i = 0; i < 3; i++) { /* Setup field 'values'. Don't allocate memory; reuse the data pointer. */ myarr->data[i].values = emxCreateWrapper_real_T(values[i], 1, values_len[i]); /* Initialize the 'sorted' field to the empty vector. */ myarr->data[i].sorted = emxCreate_real_T(1, 0); /* Initiailize the 'sum' field. */ myarr->data[i].sum = 0; } /* Call process function */ processNestedArrays(myarr); /* Print result */ for (i = 0; i < myarr->size[1]; i++) { printf(" values: "); print_vector(myarr->data[i].values); printf(" sorted: "); print_vector(myarr->data[i].sorted); printf(" sum: %.0f \n\n", myarr->data[i].sum); } /* Cleanup memory */ emxDestroyArray_myarray(myarr); /* Unused */ (void)argc; (void)argv; return 0; }

生成可执行文件并与MEX函数比较结果

使用提供的主文件,您可以为算法生成一个独立的可执行文件。

codegen配置:exearg游戏{myarray}processNestedArrays...processNestedArrays_main.c报告
代码生成成功:要查看报告,请打开('codegen/exe/processNestedArrays/html/report.mldatx')。

中定义的与独立可执行文件的输入相匹配的MEX函数的输入数据processNestedArrays_main.c

myarray =[结构(“值”, [5 3 4 1 2 6],“排序”0 (1,0),“和”0),...结构(“值”, [50 30 40 10 20 60],“排序”0 (1,0),“和”0),...结构(“值”, [42 4711 1234],“排序”0 (1,0),“和”, 0)];

将MEX函数结果与独立的可执行结果进行比较。

流(”。Mex output \n----------- \n');r = processNestedArrays_mex (myarray);disp (r (1));disp (r (2));disp (r (3));流('.exe output \n----------- \n');系统(“processNestedArrays”);
.mex output ----------- values: [5 34 12 6] sorted: [1 234 5 6] sum: 21 values: [50 30 40 10 20 60] sorted: [10 20 30 40 50 60] sum: 210 values: [42 4711 1234] sorted: [42 1234 4711] sum: 5987 .exe output ----------- /bin/bash: processNestedArrays: command not found

输出结果相同。

使用emxArray_char_T字符串输入的数据

在本例中,MATLAB函数在运行时改变字符向量的大小。由于向量的最终长度可以变化,因此生成的C代码将该向量实例化为动态大小的emxArray.这个例子展示了如何编写一个main函数emxArray_char_T用生成的函数接口。使用此示例作为使用emxArray_char_T数据类型。

MATLAB算法

这个函数replaceCats以一个字符向量作为输入,并用“velociraptor”和“velociraptor”替换单词“cat”或“cat”的所有实例。由于代码生成器不能在编译时确定输出长度,因此生成的代码使用emxArray数据类型。

函数cstrNew = replaceCats(装运箱)% # codegencstrNew =取代(装运箱,“猫”“迅猛龙”);cstrNew =取代(cstrNew,“猫”“迅猛龙”);

生成的源代码

为的生成代码replaceCats,将函数的输入类型指定为可变大小的字符数组。

t = coder.typeof (“一个”[1正]);codegenreplaceCatsarg游戏{t}报告配置:自由
代码生成成功:要查看报告,请打开('codegen/lib/replaceCats/html/report.mldatx')。

在生成的代码中,示例主文件/ codegen / lib / replaceCats /例子/ c为编写自己的主函数提供模板。

从模板中创建一个Main函数

修改main函数以从命令行接受字符输入。使用emxCreate而且emxCreateWrapperAPI函数来初始化emxArray数据。在完成编写主源文件和头文件之后,将修改后的文件放在根文件夹中。

类型main_replaceCats.c
#include "main_replaceCats.h" #include "replaceCats_terminate.h" #include "replaceCats_emxAPI.h" #include "replaceCats_initialize.h" #include  #include  #define MAX_STRING_SZ 512 static void main_replaceCats(char *inStr){/*创建emxArray的和其他变量*/ emxArray_char_T *cstr = NULL;emxArray_char_T *cstrFinal = NULL;char outStr [MAX_STRING_SZ];int initCols = (int) strlen(inStr);int finCols;/*初始化输入和输出emxArrays */ cstr = emxCreateWrapper_char_T(inStr, 1, initCols);cstrFinal = emxCreate_char_T(1,0);/*调用emxArrays上生成的代码*/ replaceCats(cstr, cstrFinal);*/ finCols = cstrFinal->size[0]*cstrFinal->size[1];if (finCols >= MAX_STRING_SZ) {printf("错误:输出字符串超过最大大小。"); exit(-1); } memcpy(outStr, cstrFinal->data, finCols); outStr[finCols]=0; /* Print output */ printf("\nOld C string: %s \n", inStr); printf( "New C string: %s \n", outStr); /* Free the emxArray memory */ emxDestroyArray_char_T(cstrFinal); } int main(int argc, char *argv[]) { if (argc != 2 ) { printf("Error: Must provide exactly one input string, e.g.\n"); printf(">replaceCats \"hello cat\"\n"); exit(-1); } replaceCats_initialize(); main_replaceCats(argv[1]); replaceCats_terminate(); return 0; }

生成可执行文件

生成可执行代码:

t = coder.typeof (“一个”[1正]);codegenreplaceCatsarg游戏{t}配置:exemain_replaceCats.c

在平台上测试可执行文件,并根据需要修改主文件。例如,在Windows上,你会得到这样的输出:

C:\>replaceCats.exe "宠物主人称自己为'猫爸'"

老C弦:宠物主人称自己为“猫爸”

新C字串:宠物主人称自己为“迅猛龙爸爸”

另请参阅

|

相关的话题