exponenta event banner

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

В большинстве случаев, когда вы генерируете код для функции MATLAB®, которая принимает или возвращает массив, сгенерированный интерфейс функции C/C++ содержит массив. Чтобы использовать сгенерированные функциональные интерфейсы, изучите, как сгенерированные массивы C/C++ заданы и созданы. В частности, учитесь использовать структуру данных emxArray, которая сгенерирована, чтобы представлять динамически выделенные массивы.

Когда вы генерируете код C/C++, пример, основной файл создается, который показывает, как использовать массивы со сгенерированным функциональным кодом. Можно использовать пример, основной в качестве шаблона или отправной точки для собственного приложения.

Реализация массивов в сгенерированном коде 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)

Определение динамической структуры данных 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. <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-th элемент вектора размера хранит длину i-th размерности массива.
int allocatedSizeКоличество элементов памяти выделяется для массива. Если размер массивов изменяется, сгенерированный код перераспределяет память на основе нового размера.
int numDimensionsДлина вектора размера. Количество размерностей можно получить доступ, не пересекаясь в освобожденную или неиспользованную память.
boolean_T canFreeData

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

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

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

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

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

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

Эта таблица показывает список экспортируемых API-функций emxArray. Некоторые 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)

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

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

Примеры

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

Рассмотрите функцию MATLAB, myuniquetol от Генерирует Код для Данных Переменного Размера.

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 при помощи функций emxInitArray или emxCreate

emxCreate и API-функции emxCreateND создают 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.

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

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

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

emxCreateWrapper и API-функции emxCreateWrapperND позволяют вам загрузить или перенести существующую память и данные в 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

Осмотрите сгенерированные функциональные интерфейсы

Исходный код 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 файла сгенерированного примера показывает, как вызвать сгенерированный функциональный код путем создания и инициализации входных параметров с API-функцией emxCreate.

Запишите и используйте свой собственный индивидуально настраиваемый основной файл, чтобы инициализировать данные emxArray

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

Файл processNestedArrays_main.c показывает пример. Этот основной файл использует API-функции emxArray, чтобы создать и инициализировать данные о структуре. И для сгенерированного примера основной файл и для этого рукописного основного файла, код инициализирует данные 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' с '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 и API-функции emxCreateWrapper, чтобы инициализировать ваши 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

Протестируйте исполняемый файл на своей платформе и измените ваш основной файл по мере необходимости. Например, на 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'

Смотрите также

|

Похожие темы