Main Content

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 an emxArray 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 the emxArray 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 <myFunction>_types.h from the code generation report. <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.

FieldDescription
<type> *dataPointer to an array of elements of type <type>.
int *sizePointer to a size vector. The i-th element of the size vector stores the length of the i-th dimension of the array.
int allocatedSizeNumber of memory elements allocated for the array. If the array size changes, the generated code reallocates memory based on the new size.
int numDimensionsLength 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 emxArray processing routines.

  • true – The generated code deallocates memory on its own.

  • false – The program that instantiates the emxArray must manually deallocate the memory pointed to by data.

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 <myFunction>_emxAPI.h in your C code. <myFunction> is the name of your entry-point function. Other functions produced by the code generator that operate on emxArray data, defined in <myFunction>_emxutil.h, are not intended for manual use.

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 FunctionDescription

emxArray_<name> *emxCreate_<name>(int rows, int cols)

Creates a pointer to a two-dimensional emxArray, with data elements initialized to zero. Allocates new memory for the data.

emxArray_<name> *emxCreateND_<name>(int numDimensions, int *size)

Creates a pointer to an N-dimensional emxArray, with data elements initialized to zero. Allocates new memory for the data.

emxArray_<name> *emxCreateWrapper_<name>(<type> *data, int rows, int cols)

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.

emxArray_<name> *emxCreateWrapperND_<name>(<type> *data, int numDimensions, int *size)

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.

void emxInitArray_<name>(emxArray_<name> **pEmxArray, int numDimensions)

Allocates memory for a double pointer to an emxArray.

void emxDestroyArray_<name>(emxArray_<name> *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'

See Also

|

Related Topics