exponenta event banner

Использование массивов 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])

Поместите их на вектор строки переменного размера, ограниченный 30000 элементами.

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

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-й элемент вектора размера хранит длину 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 в коде C. <myFunction> - имя функции начальной точки. Другие функции, создаваемые генератором кода, которые работают на emxArray данные, определенные в <myFunction>_emxutil.h, не предназначены для ручного использования.

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

В этой таблице представлен список экспортированных 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 из команды «Создать код для данных переменного размера».

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 элементы в коде C, только 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 Функции

emxCreate и emxCreateND Функции API создают emxArray, выделение новой памяти из кучи по мере необходимости. Затем можно использовать emxArray как вход или выход из сгенерированного кода. В этом примере кода C показано, как использовать 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

emxCreateWrapper и emxCreateWrapperND Функции API позволяют загружать или переносить существующую память и данные в emxArray для передачи данных сгенерированной функции. В этом примере кода C показано, как использовать 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'

См. также

|

Связанные темы