Используйте массивы 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.

Фиксированный размер, ограниченный порогом.

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])

Нажмите таковые на вектор-строку переменного размера, ограниченную 300 элементами.

Переменный размер, ограниченный порогом.

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])

Нажмите таковые на вектор-строку переменного размера, ограниченную 30 000 элементами.

Переменный размер, не ограниченный порогом.

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)

Создайте массив с размером, определенным неограниченным целым входом.

Неизвестный и неограниченный во время компиляции.

function y = create_vec3(n) %#codegen
y = int8(ones(1,n));
void create_vec3(int n, emxArray_int8_T *y)

The emxArray Определение динамической структуры данных

В сгенерированном коде C/C + +, emxArray определение структуры данных зависит от типа данных элементов, которые она хранит. Общее определение принимает форму:

struct emxArray_<name> 
{
    <type> *data;
    int *size;
    int allocatedSize;
    int numDimensions;
    boolean_T canFreeData;
}; 

В определении <type> указывает тип данных и <name> указывает имя, используемое для идентификации emxArray структура. Генератор кода выбирает <name> на основе типов, определенных для генерации кода MEX, как указано в отображении типов MATLAB с типами в сгенерированном коде».

В качестве примера рассмотрим emxArray определение сгенерировано для функции create_vec2. The <name> является emxArray_real_T и <type> является double.

struct emxArray_real_T
{
  double *data;
  int *size;
  int allocatedSize;
  int numDimensions;
  boolean_T canFreeData;
};

Не пытайтесь предсказать значения для <type> и <name> до генерации кода. Вместо этого, после завершения генерации кода, смотрите файл <myFunction>_types.h из отчета генерации кода. <myFunction> - имя функции точки входа.

Сгенерированный код может также задать emxArray структура при помощи typedef операторы, как в этих примерах.

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;

Эта таблица описывает emxArray структурные поля.

ОбластьОписание
<type> *dataУказатель на массив элементов типа <type>.
int *sizeУказатель на вектор размера. i-й элемент вектора размера хранит длину i-го размерности массива.
int allocatedSizeКоличество элементов памяти, выделенных для массива. Если размер массива изменяется, сгенерированный код перераспределяет память на основе нового размера.
int numDimensionsДлина вектора размера. Количество размерностей, к которым вы можете получить доступ, не пересекаясь с неиспользованной или неиспользованной памятью.
boolean_T canFreeData

Логический флаг, указывающий, как отменить выделение памяти. Используется только внутренним emxArray стандартные программы обработки.

  • true - Сгенерированный код самостоятельно освобождает память.

  • false - Программа, которая создает экземпляры emxArray необходимо вручную отменить выделение памяти, на которую указывает data.

Служебные функции для взаимодействия с emxArray Данные

Создание и взаимодействие со emxArray данные в коде C/C + +, генератор кода экспортирует набор вспомогательных функций C/C + + с удобным API. Используйте эти функции, чтобы убедиться, что вы правильно инициализируете и уничтожите emxArray типы данных. Чтобы использовать эти функции, вставьте оператор include для сгенерированного файла заголовка <myFunction>_emxAPI.h в вашем Коде С. <myFunction> - имя функции точки входа. Другие функции, произведенные генератором кода, которые работают с emxArray данные, заданные в <myFunction>_emxutil.h, не предназначены для ручного использования.

Пример основного файла, сгенерированного по умолчанию для lib, dll, и exe код включает вызовы на emxArray Функции API. Пример основного кода инициализирует emxArray данные к обобщенным нулевым значениям. Чтобы использовать фактические входы и значения данных, измените пример основного файла или создайте свой собственный основной файл. Для получения дополнительной информации об использовании основной функции, смотрите Включить сгенерированный код Используя Пример Основной функции.

В этой таблице показан список экспортированных emxArray Функции API. Некоторые функции API принимают начальное количество строк, столбцов или размерностей для emxArray данные. Каждая размерность может расти, чтобы включать новые данные по мере необходимости.

emxArray Функция помощникаОписание

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

Создание указателя на двумерное emxArray, с элементами данных, инициализированными в нуль. Выделяет новую память для данных.

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

Создание указателя на N-мерную emxArray, с элементами данных, инициализированными в нуль. Выделяет новую память для данных.

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

Создание указателя на двумерное emxArray. Использует данные и память, которую вы предоставляете, и переносит их в emxArray структура данных. Устанавливает canFreeData на false чтобы предотвратить непреднамеренное освобождение пользовательской памяти.

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

Создание указателя на N-мерную emxArray. Использует данные и память, которую вы предоставляете, и переносит их в emxArray структура данных. Устанавливает canFreeData на false чтобы предотвратить непреднамеренное освобождение пользовательской памяти.

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

Выделяет память для двойного указателя на emxArray.

void emxDestroyArray_<name>(emxArray_<name> *emxArray)

Освобождает динамическую память, выделенную emxCreate или emxInitArray функций.

Генератор кода экспортирует emxArray Функции API только для массивов, которые являются аргументами функции точки входа или которые используются функциями, вызываемыми coder.ceval.

Примеры

Используйте Интерфейс функции для статически выделенного массива

Рассмотрим функцию MATLAB myuniquetol из Generate Code для данных переменного размера.

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

Сгенерируйте код для myuniquetol. Использовать coder.typeof чтобы задать типы входа как ограниченный массив переменного размера и скаляра двойной точности.

codegen -config:lib -report myuniquetol -args {coder.typeof(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])

Интерфейс функции объявляет входной параметр A и выходной аргумент B. A_size содержит размер A. После вызова к myuniquetol, B_size содержит размер B.

Использование B_size для определения количества элементов B к которому можно получить доступ после вызова myuniquetol. B_size[0] содержит размер первой размерности. B_size[1] содержит размер второго измерения. Поэтому количество элементов B является B_size[0]*B_Size[1]. Хотя B имеет 100 элементов в коде С, только B_size[0]*B_Size[1] элементы содержат действительные данные.

Эта основная функция C показывает, как вызвать 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);
}

Создайте emxArray При помощи emxCreate или emxInitArray Функции

The emxCreate и emxCreateND Функции API создают emxArray, выделение новой памяти из кучы по мере необходимости. Затем можно использовать emxArray как вход или вывод из сгенерированного кода. Этот пример кода С показывает, как использовать emxCreate. Предположим, что вы уже сгенерировали исходный код для функции myFunction который использует тип данных 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;
}

В этом примере вы знаете начальный размер emxArray. Если вы не знаете размер массива, как когда вы используете массив для хранения выхода, можно ввести значение 0 для rows и cols поля. Например, если вы не знаете количество столбцов, можно записать:

emxArray_uint32_T *pEmx = emxCreate_uint32_T(10,0);

Структура данных растет, чтобы включать данные по мере необходимости. После запусков функции определите размер выхода путем доступа к size и numDimensions поля.

Используйте emxInitArray Функция API для создания массива, который возвращается как выход, для которого вы не знаете размер массива усовершенствование. Например, чтобы создать emxArray двух размерностей, с неизвестными размерами в любой размерности, можно записать:

emxArray_uint32_T *s;
emxInitArray_uint32_T(&s, 2);

Загрузка существующих данных в emxArray

The emxCreateWrapper и emxCreateWrapperND Функции API позволяют вам загружать или переносить существующую память и данные в emxArray для передачи данных в сгенерированную функцию. Этот пример кода С показывает, как использовать emxCreateWrapper. Предположим, что вы уже сгенерировали исходный код для функции myFunction который использует тип данных 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;
}

Создание и использование вложенных emxArray Данные

В этом примере показано, как работать с сгенерированным кодом, который содержит emxArray данные, вложенные в другие emxArray данные. Чтобы использовать сгенерированный код, в вашей основной функции или вызывающей функции инициализируйте emxArray данные из нижних узлов вверх.

Алгоритм MATLAB

Этот алгоритм MATLAB итерируется через массив структур, называемых myarray. Каждая структура содержит более низкоуровневый массив значений. Алгоритм сортирует и суммирует элементы массива нижнего уровня для каждой 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

Сгенерируйте MEX-функцию для проверки

В качестве первого шага, чтобы иметь возможность протестировать алгоритм, сгенерируйте MEX-функцию. Используйте coder.typeof функция, чтобы вручную задать вход как неограниченный, переменный размер вектора-строки structs, которые сами содержат неограниченные векторы-строки переменного размера.

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.

Сгенерированные интерфейсы функций

Исходный код MEX-функции содержит специализированный код, который позволяет ему взаимодействовать со окружением выполнения MATLAB, что делает его более сложным для чтения. Чтобы создать более упрощенный исходный код, сгенерируйте библиотечный код.

codegen -config:lib -args {myarray} processNestedArrays -report
Code generation successful: To view the report, open('codegen/lib/processNestedArrays/html/report.mldatx').

Проверьте сгенерированный код функции processNestedArrays.c из отчета генерации кода. Сгенерированный пример основного файла main.c показывает, как вызвать сгенерированный код функции путем создания и инициализации входов с emxCreate Функция API.

Запись и использование собственного настраиваемого основного файла для инициализации emxArray Данные

Несмотря на то, что сгенерированный пример main показывает, как вызвать сгенерированный код функции, он не содержит информацию о желаемых входных значениях. Используя пример main в качестве руководства, запишите свой собственный основной файл. Используйте стиль и настройки кодирования по своему выбору. Укажите значения ваших входов и при необходимости вставьте код перед и после обработки.

Файл processNestedArrays_main.c показывает пример. Этот основной файл использует emxArray Функции API для создания и инициализации данных структуры. Для сгенерированного основного файла примера и этого основного файла, записанного вручную, код инициализирует emxArray данные в нижних (листовых) узлах и присваивает эти данные вышеприведенным узлам.

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;
}

Сгенерируйте исполняемый файл и сравните результаты с MEX-функцией

Используя предоставленный основной файл, можно сгенерировать независимый исполняемый файл для алгоритма.

codegen -config:exe -args {myarray} processNestedArrays ...
    processNestedArrays_main.c -report
Code generation successful: To view the report, open('codegen/exe/processNestedArrays/html/report.mldatx').

Объявите входные данные для MEX-функции, которые совпадают с входами для независимого исполняемого файла, определенного в 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)];

Сравните результаты MEX-функции с независимыми исполняемыми результатами.

fprintf('.mex output \n----------- \n');
r = processNestedArrays_mex(myarray);
disp(r(1));
disp(r(2));
disp(r(3));

fprintf('.exe output \n----------- \n');
system('processNestedArrays');
.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 
----------- 
/bin/bash: processNestedArrays: command not found

Результаты выхода идентичны.

Использование emxArray_char_T Данные с строковыми входами

В этом примере функция MATLAB изменяет размер вектора символов во время исполнения. Поскольку окончательная длина вектора может варьироваться, сгенерированный код C создает экземпляр вектора как динамический размер emxArray. В этом примере показано, как написать основную функцию, которая использует emxArray_char_T с сгенерированным интерфейсом функции. Используйте этот пример в качестве руководства для работы со emxArray_char_T тип данных.

Алгоритм MATLAB

Функция replaceCats принимает вектор символов в качестве входов и заменяет все образцы слова 'cat' или 'Cat' на 'velociraptor' и 'Velociraptor'. Поскольку генератор кода не может определить длину выхода во время компиляции, сгенерированный код использует emxArray тип данных.

function cstrNew = replaceCats(cstr)
%#codegen
cstrNew = replace(cstr,'cat','velociraptor');
cstrNew = replace(cstrNew,'Cat','Velociraptor');

Сгенерируйте исходный код

Чтобы сгенерировать код для replaceCats, задайте тип входа для функции как символьного массива переменного размера.

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').

В сгенерированном коде пример основного файла /codegen/lib/replaceCats/examples/main.c предоставляет шаблон для написания собственной основной функции.

Создайте основную функцию из шаблона

Измените основную функцию, чтобы взять ввод символов из командной строки. Используйте emxCreate и emxCreateWrapper API-функции для инициализации данных emxArray. После завершения записи основного исходного файла и файла заголовка поместите измененные файлы в корневую папку.

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;
}

Сгенерируйте исполняемый файл

Сгенерируйте исполняемый код:

t = coder.typeof('a',[1 inf]);
codegen replaceCats -args {t} -config:exe main_replaceCats.c
Code generation successful.

Протестируйте исполняемый файл на вашей платформе и измените свой основной файл по мере необходимости. Для примера в Windows вы получаете выход:

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'

См. также

|

Похожие темы