Use C Arrays in the Generated Function Interfaces
In most cases, when you generate code for a MATLAB® function that accepts or returns an array, the generated C/C++ function interface contains an array. To use the generated function interfaces, learn how the generated C/C++ arrays are defined and constructed. In particular, learn to use the emxArray
data structure that is generated to represent dynamically allocated arrays.
When you generate C/C++ code, an example main file is created that shows how to use arrays with the generated function code. You can use the example main as a template or starting point for your own application.
Implementation of Arrays in the Generated C/C++ Code
The code generator produces C/C++ array definitions that depend on the array element type and whether the array uses static or dynamic memory allocation. The two kinds of memory allocation for an array require two different implementations:
For an array whose size is bounded within a predefined threshold, the generated C/C++ definition consists of a pointer to memory and an integer that stores the total number of array elements, the array size. The memory for this array comes from the program stack and is statically allocated.
For an array whose size is unknown and unbounded at compile time, or whose bound exceeds a predefined threshold, the generated C/C++ definition consists of a data structure called an
emxArray
. When anemxArray
is created, intermediate storage bounds are set based on the current array size. During program execution, as intermediate storage bounds are exceeded, the generated code appropriates additional memory space from the heap and adds it to theemxArray
storage. The memory for this array is dynamically allocated.
By default, arrays that are bounded within a threshold size do not use dynamic allocation in the generated code. Alternatively, you can disable dynamic memory allocation and change the dynamic memory allocation threshold. See Control Memory Allocation for Variable-Size Arrays.
This table lists a few typical cases for array representation in the generated code.
Algorithm Description and Array Size | MATLAB Function | Generated C Function Interface |
---|---|---|
Place ones onto a fixed-size 1-by-500 row vector. Fixed-size, bounded within threshold. |
function B = create_vec0 %#codegen B = zeros(1,500); j = 1; for i = 1:500 if round(rand) B(1,j) = 1; j = j + 1; end end |
void create_vec0(double B[500]) |
Push ones onto a variable-size row vector bounded at 300 elements. Variable-size, bounded within threshold. |
function B = create_vec %#codegen B = zeros(1,0); coder.varsize('B',[1 300],[0 1]); for i = 1:500 if round(rand) B = [1 B]; end end |
void create_vec(double B_data[],... int B_size[2]) |
Push ones onto a variable-size row vector bounded at 30,000 elements. Variable-size, not bounded within threshold. |
function B = create_vec2 %#codegen B = zeros(1,0); coder.varsize('B',[1 30000],[0 1]); for i = 1:500 if round(rand) B = [1 B]; end end |
void create_vec2(emxArray_real_T *B) |
Create an array with size determined by an unbounded integer input. Unknown and unbounded at compile time. |
function y = create_vec3(n) %#codegen y = int8(ones(1,n)); |
void create_vec3(int n,... emxArray_int8_T *y) |
The emxArray
Dynamic Data Structure Definition
In the generated C/C++ code, the emxArray
data structure definition depends on the data type of the elements that it stores. The general definition takes the form:
struct emxArray_<name> { <type> *data; int *size; int allocatedSize; int numDimensions; boolean_T canFreeData; };
In the definition, <type>
indicates a data type and <name>
indicates a name used to identify the emxArray
structure. The code generator chooses <name>
based on the types defined for MEX code generation, as listed in Mapping MATLAB Types to Types in Generated Code.
As an example, consider the emxArray
definition generated for the function create_vec2
. The <name>
is emxArray_real_T
and the <type>
is double
.
struct emxArray_real_T { double *data; int *size; int allocatedSize; int numDimensions; boolean_T canFreeData; };
Do not seek to predict the entries for <type>
and <name>
prior to code generation. Instead, after code generation is complete, inspect the file
from the code generation report. <myFunction>
_types.h<myFunction>
is the name of your entry-point function.
The generated code can also define the emxArray
structure by using typedef
statements, as in these examples.
typedef struct { emxArray_real_T *f1; } cell_wrap_0; typedef struct { cell_wrap_0 *data; int *size; int allocatedSize; int numDimensions; boolean_T canFreeData; } emxArray_cell_wrap_0;
This table describes the emxArray
structure fields.
Field | Description |
---|---|
<type> *data | Pointer to an array of elements of type <type> . |
int *size | Pointer to a size vector. The i-th element of the size vector stores the length of the i-th dimension of the array. |
int allocatedSize | Number of memory elements allocated for the array. If the array size changes, the generated code reallocates memory based on the new size. |
int numDimensions | Length of the size vector. The number of dimensions you can access without crossing into unallocated or unused memory. |
boolean_T canFreeData | Boolean flag indicating how to deallocate memory. Used only by the internal
|
Utility Functions for Interacting with emxArray
Data
To create and interact with the emxArray
data in your C/C++ code, the code generator exports a set of C/C++ helper functions with a user-friendly API. Use these functions to ensure that you properly initialize and destroy emxArray
data types. To use these functions, insert an include statement for the generated header file
in your C code. <myFunction>
_emxAPI.h<myFunction>
is the name of your entry-point function. Other functions produced by the code generator that operate on emxArray
data, defined in
, are not intended for manual use.<myFunction>
_emxutil.h
The example main file generated by default for lib
, dll
, and exe
code includes calls to the emxArray
API functions. The example main code initializes the emxArray
data to generic zero values. To use actual data inputs and values, modify the example main or create your own main file. For more information on using a main function, see Incorporate Generated Code Using an Example Main Function.
This table shows the list of exported emxArray
API functions. Some of the
API functions accept the initial number of rows, columns, or dimensions for the
emxArray
data. Each dimension can grow to accommodate new data as needed.
emxArray
arrays instantiated by using pointers keep a copy of the
input values. Changing the values of the input variables during run-time does not change
the size of the emxArray
.
emxArray Helper Function | Description |
---|---|
| Creates a pointer to a two-dimensional emxArray , with data elements
initialized to zero. Allocates new memory for the data. |
| Creates a pointer to an N-dimensional emxArray , with data elements
initialized to zero. Allocates new memory for the data. |
| Creates a pointer to a two-dimensional emxArray . Uses data and memory you
provide and wraps it into the emxArray data
structure. Sets canFreeData to
false to prevent inadvertent freeing of user
memory. |
| Creates a pointer to an N-dimensional emxArray . Uses data and memory you
provide and wraps it into the emxArray data
structure. Sets canFreeData to
false to prevent inadvertent freeing of user
memory. |
| Allocates memory for a double pointer to an emxArray . |
| Frees dynamic memory allocated by the emxCreate or emxInitArray functions. |
The code generator exports the emxArray
API functions only for arrays that
are entry-point function arguments or that are used by functions called by
coder.ceval
.
Examples
Use the Function Interface for a Statically Allocated Array
Consider the MATLAB function myuniquetol
from Generate Code for Variable-Size Data.
function B = myuniquetol(A, tol) %#codegen A = sort(A); coder.varsize('B', [1 100], [0 1]); B = zeros(1,0); k = 1; for i = 2:length(A) if abs(A(k) - A(i)) > tol B = [B A(i)]; k = i; end end
Generate code for myuniquetol
. Use
coder.typeof
to specify the input types as a bounded,
variable-size array and a scalar double.
codegen -config:lib -report myuniquetol -args {coder.typeof(0,[1 100],[0 1]),coder.typeof(0)}
The statement coder.varsize('B', [1 100], [0 1])
specifies that
B
is a variable-size array whose first dimension is fixed at
1 and whose second dimension can vary up to 100 elements. Because the maximum size
of array B
is bounded within the default threshold size, the code
generator uses static memory allocation for the array.
The generated function interface is:
void myuniquetol(const double A_data[], const int A_size[2], double tol, double B_data[], int B_size[2])
The function interface declares the input argument A
and the
output argument B
. A_size
contains the size of
A
. After the call to myuniquetol
,
B_size
contains the size of B
.
Use B_size
to determine the number of elements of
B
that you can access after the call to
myuniquetol
. B_size[0]
contains the size
of the first dimension. B_size[1]
contains the size of the second
dimension. Therefore, the number of elements of B
is
B_size[0]*B_Size[1]
. Even though B
has
100
elements in the C code, only
B_size[0]*B_Size[1]
elements contain valid data.
This C main function shows how to call
myuniquetol
.
void main() { double A[100], B[100]; int A_size[2] = { 1, 100 }; int B_size[2]; int i; for (i = 0; i < 100; i++) { A[i] = (double)1/i; } myuniquetol(A, A_size, 0.1, B, B_size); }
Create an emxArray
by Using the emxCreate
or emxInitArray
Functions
The emxCreate
and emxCreateND
API functions create an
emxArray
, allocating new memory from the heap as
needed. You can then use the
emxArray
as an input to or output from the generated code.
This C code example shows how to use emxCreate
. Assume that you
have already generated source code for a function myFunction
that
uses the data type emxArray_uint32_T
.
#include <stdio.h> #include <stdlib.h> #include "myFunction_emxAPI.h" #include "myFunction.h" int main(int argc, char *argv[]) { /* Create a 10-by-10 uint32_T emxArray */ emxArray_uint32_T *pEmx = emxCreate_uint32_T(10,10); /* Initialize the emxArray memory, if needed */ int k = 0; for (k = 0; k < 100; ++k) { pEmx->data[k] = (uint32_T) k; } /* Use pEmx array here; */ /* Insert call to myFunction */ /* Deallocate any memory allocated in pEmx */ /* This DOES free pEmx->data */ emxDestroyArray_uint32_T(pEmx); /* Unused */ (void)argc; (void)argv; return 0; }
In this example, you know the initial size of the emxArray
. If you do not know the size of the array, as when you use the array to store output, you can enter the value 0 for the rows
and cols
fields. For example, if you do not know the number of columns, you can write:
emxArray_uint32_T *pEmx = emxCreate_uint32_T(10,0);
The data structure grows to accommodate data as needed. After your function runs, determine the output size by accessing the size
and numDimensions
fields.
Use the emxInitArray
API function to create an array that is returned as output, for which you do not know the array size in advance. For example, to create an emxArray
of two dimensions, with unknown sizes in either dimension, you can write:
emxArray_uint32_T *s; emxInitArray_uint32_T(&s, 2);
Load Existing Data into an emxArray
The emxCreateWrapper
and emxCreateWrapperND
API
functions enable you to load or wrap existing memory and data into an
emxArray
to pass the data to a generated function. This C
code example shows how to use emxCreateWrapper
. Assume that you
have already generated source code for a function myFunction
that
uses the data type emxArray_uint32_T
.
#include <stdio.h> #include <stdlib.h> #include "myFunction_emxAPI.h" #include "myFunction.h" int main(int argc, char *argv[]) { /* Create a 10-by-10 C array of uint32_T values */ uint32_T x[100]; int k = 0; emxArray_uint32_T *pEmx = NULL; for (k = 0; k < 100; k++) { x[k] = (uint32_T) k; } /* Load existing data into an emxArray */ pEmx = emxCreateWrapper_uint32_T(x,10,10); /* Use pEmx here; */ /* Insert call to myFunction */ /* Deallocate any memory allocated in pEmx */ /* This DOES NOT free pEmx->data because the wrapper function was used */ emxDestroyArray_uint32_T(pEmx); /* Unused */ (void)argc; (void)argv; return 0; }
Create and Use Nested emxArray
Data
This example shows how to work with generated code that contains emxArray
data nested inside of other emxArray
data. To use the generated code, in your main function or calling function, initialize the emxArray
data from the bottom nodes up.
MATLAB Algorithm
This MATLAB algorithm iterates through an array of structures called myarray
. Each structure contains a lower-level array of values. The algorithm sorts and sum the elements of the lower-level array for each struct
.
% y is an array of structures of the form % struct('values', [...], 'sorted', [...], 'sum', ... ) function y = processNestedArrays(y) %#codegen coder.cstructname(y, 'myarray'); for i = 1:numel(y) y(i).sorted = sort(y(i).values); y(i).sum = sum(y(i).values); end
Generate MEX Function for Testing
As a first step, to be able to test the algorithm, generate a MEX function. Use the coder.typeof
function to manually specify the input as an unbounded, variable-size row vector of structs
, which themselves contain unbounded, variable-size row vectors.
myarray = coder.typeof( ... struct('values', coder.typeof(0, [1 inf]), ... 'sorted', coder.typeof(0, [1 inf]), ... 'sum', coder.typeof(0)) , [1 inf]); codegen -args {myarray} processNestedArrays
Code generation successful.
Inspect the Generated Function Interfaces
The MEX function source code contains specialized code that enables it to interface with the MATLAB runtime environment, which makes it more complex to read. To produce more simplified source code, generate library code.
codegen -config:lib -args {myarray} processNestedArrays -report
Code generation successful: To view the report, open('codegen/lib/processNestedArrays/html/report.mldatx')
Inspect the generated function code processNestedArrays.c
from the code generation report. The generated example main file main.c
shows how to call the generated function code by creating and initializing inputs with the emxCreate
API function.
Write and Use Your Own Customized Main File to Initialize emxArray
Data
Although the generated example main shows how to invoke the generated function code, it does not contain information on desired input values. Using the example main as a guide, write your own main file. Use the coding style and preferences of your choice. Specify the values of your inputs and insert pre and post-processing code as needed.
The file processNestedArrays_main.c
shows an example. This main file uses the emxArray
API functions to create and initialize the structure data. For both the generated example main file and this hand written main file, the code initializes the emxArray
data at the bottom (leaf) nodes, and assigns that data to the nodes above.
type processNestedArrays_main.c
#include <stdio.h> #include <stdlib.h> #include "processNestedArrays_emxAPI.h" #include "processNestedArrays.h" static void print_vector(emxArray_real_T *v) { int i; printf("["); for (i = 0; i < v->size[1]; i++) { if (i > 0) printf(" "); printf("%.0f", v->data[i]); } printf("] \n"); } int main(int argc, char *argv[]) { int i; static double values_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; }
Generate an Executable and Compare Results with MEX Function
Using the provided main file, you can generate a standalone executable for the algorithm.
codegen -config:exe -args {myarray} processNestedArrays ... processNestedArrays_main.c -report
Code generation successful: To view the report, open('codegen/exe/processNestedArrays/html/report.mldatx')
Declare input data for the MEX function that matches the input for the standalone executable, defined in processNestedArrays_main.c
.
myarray = [struct('values', [5 3 4 1 2 6], 'sorted', zeros(1,0), 'sum', 0), ... struct('values', [50 30 40 10 20 60], 'sorted', zeros(1,0), 'sum', 0), ... struct('values', [42 4711 1234], 'sorted', zeros(1,0), 'sum', 0)];
Compare the MEX function results with the standalone executable results.
fprintf('.mex output \n----------- \n'); r = processNestedArrays_mex(myarray); disp(r(1)); disp(r(2)); disp(r(3)); fprintf('.exe output \n----------- \n'); if isunix system('./processNestedArrays') elseif ispc system('processNestedArrays.exe') else disp('Platform is not supported') end
.mex output ----------- values: [5 3 4 1 2 6] sorted: [1 2 3 4 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 ----------- values: [5 3 4 1 2 6] sorted: [1 2 3 4 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 ans = 0
The output results are identical.
Use emxArray_char_T
Data with String Inputs
In this example, a MATLAB function changes the size of a character vector at run time. Because the final length of the vector can vary, the generated C code instantiates the vector as a dynamically sized emxArray
. This example shows how to write a main function that uses emxArray_char_T
with the generated function interface. Use this example as a guide for working with the emxArray_char_T
data type.
MATLAB Algorithm
The function replaceCats
takes a character vector as input and replaces all instances of the word 'cat' or 'Cat' with 'velociraptor' and 'Velociraptor'. Because the code generator cannot determine the output length at compile time, the generated code uses the emxArray
data type.
function cstrNew = replaceCats(cstr) %#codegen cstrNew = replace(cstr,'cat','velociraptor'); cstrNew = replace(cstrNew,'Cat','Velociraptor');
Generate Source Code
To generate code for replaceCats
, specify the input type to the function as a variable-size character array.
t = coder.typeof('a',[1 inf]); codegen replaceCats -args {t} -report -config:lib
Code generation successful: To view the report, open('codegen/lib/replaceCats/html/report.mldatx')
In the generated code, the example main file /codegen/lib/replaceCats/examples/main.c
provides a template for writing your own main function.
Create a Main Function from the Template
Modify the main function to take character input from the command line. Use the emxCreate
and emxCreateWrapper
API functions to initialize your emxArray data. After you have finished writing your main source file and header file, place the modified files in the root folder.
type main_replaceCats.c
#include "main_replaceCats.h" #include "replaceCats.h" #include "replaceCats_terminate.h" #include "replaceCats_emxAPI.h" #include "replaceCats_initialize.h" #include <string.h> #include <stdio.h> #define MAX_STRING_SZ 512 static void main_replaceCats(char *inStr) { /* Create emxArray's & other variables */ emxArray_char_T *cstr = NULL; emxArray_char_T *cstrFinal = NULL; char outStr[MAX_STRING_SZ]; int initCols = (int) strlen(inStr); int finCols; /* Initialize input & output emxArrays */ cstr = emxCreateWrapper_char_T(inStr, 1, initCols); cstrFinal = emxCreate_char_T(1, 0); /* Call generated code on emxArrays */ replaceCats(cstr, cstrFinal); /* Write output string data with null termination */ finCols = cstrFinal->size[0]*cstrFinal->size[1]; if (finCols >= MAX_STRING_SZ) { printf("Error: Output string exceeds max size."); 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; }
Generate Executable File
Generate executable code:
t = coder.typeof('a',[1 inf]); codegen replaceCats -args {t} -config:exe main_replaceCats.c
Code generation successful.
Test the executable on your platform and modify your main file as needed. For example, on Windows, you get the output:
C:\>replaceCats.exe "The pet owner called themselves a 'Catdad'"
Old C string: The pet owner called themselves a 'Catdad'
New C string: The pet owner called themselves a 'Velociraptordad'