MATLAB® Data API, используемый MEX-функциями C++, обеспечивает ту же семантику копирования при записи, которая используется функциями, написанными непосредственно в MATLAB. MEX-функция может копировать массив данных, переданный ему, но эта копия является разделяемой копией исходной переменной. Отдельная копия данных сделана, когда MEX-функция изменяет значения в массиве.
То, когда вы вызываете MEX-функцию, входные параметры matlab::data::Array
к MEX-функциям являются совместно использованными копиями переменной MATLAB, раньше вызывало MEX-функцию. Разделяемые копии обеспечивают преимущество когда MEX-функция:
Не изменяет большой массив, переданный MEX-функции.
Изменяет определенные поля структуры, которая передается как вход, не делая копию целой структуры.
Изменяет ячейку в массиве ячеек, который передается как вход, не вызывая глубокую копию массива ячеек
Изменяет свойство объекта, которое передается как вход, не делая глубокую копию объекта.
Этот пример кода извлекает выгоду из использования разделяемых копий при вычислении среднего значения матрицы изображений. Копирование входного массива к
matlab::data::TypedArray<T>
const
, который поддерживает итераторы, позволяет использованию основанного на области значений цикла for
выполнить вычисление, но гарантирует, что массив не копируется.
#include "mex.hpp" #include "mexAdapter.hpp" using matlab::mex::ArgumentList; using namespace matlab::data; class MexFunction : public matlab::mex::Function { ArrayFactory factory; public: void operator()(ArgumentList outputs, ArgumentList inputs) { double sm = 0; const TypedArray<uint8_t> inArray = inputs[0]; for (auto& elem : inArray) { sm += elem; } outputs[0] = factory.createScalar(sm / inArray.getNumberOfElements()); } };
Можно использовать эту функцию, чтобы найти среднее значение массива топографических данных. Загрузите файл topo.mat
в рабочее пространство MATLAB и передайте массив данных MEX-функции. Сохраните исходный код MEX-функции в файле под названием aveImage.cpp
.
mex aveImage.cpp load topo m = aveImage(topo) m = -1.8948e+03
Когда вы передаете массив ячеек MEX-функции, представлением MATLAB Data API его, matlab::data::CellArray
, является по существу matlab::data::Array
, который содержит matlab::data::Array
в каждой ячейке. Этот проект позволяет каждому содержавшему массиву быть разделяемой копией, пока не изменено.
Когда вы изменяете ячейку массива ячеек в MEX-функции, только массив, содержавшийся в той ячейке, не разделен, не целый массив ячеек. Например, предположите, что вы копируете массив ячеек в массив ячеек B. Значение каждой ячейки совместно используется.
Если вы изменяете Cell1
в массиве ячеек B, массив в той ячейке больше не совместно используется с Cell1
в массиве ячеек A. Однако значения всех других ячеек остаются разделяемыми так, чтобы целый массив ячеек не должен был быть скопирован.
Этот пример показывает, как передать массив ячеек MEX-функции и изменить одну ячейку в массиве ячеек, не вызывая копии данных в других ячейках. Пример загружает данные изображения в рабочее пространство MATLAB и создает массив ячеек, который содержит данные изображения, заголовок и палитру.
load durer
whos
Name Size Bytes Class X 648x509 2638656 double caption 2x28 112 char map 128x3 3072 double
Присвойте каждую переменную различной ячейке.
durerCell{1} = X; durerCell{2} = caption; durerCell{3} = map;
Эта MEX-функция использует std::move
, чтобы переместить входной массив ячеек в matlab::data::CellArray
, который обеспечивает доступ к отдельным ячейкам. Единственные измененные данные являются заголовком, который хранится в одной ячейке массива ячеек. Изменить ту ячейку:
Получите ссылку matlab::data::Reference<TypedArray<T>>
на массив в ячейке, содержащей заголовок.
Присвойте новое значение ссылке как matlab::data::CharArray
, потому что возвращенное значение является вектором символов MATLAB.
#include "mex.hpp" #include "mexAdapter.hpp" using matlab::mex::ArgumentList; using namespace matlab::data; class MexFunction : public matlab::mex::Function { ArrayFactory factory; public: void operator()(ArgumentList outputs, ArgumentList inputs) { checkArguments(inputs); CellArray imageCell = std::move(inputs[0]); TypedArrayRef<char16_t> cellRef = imageCell[1]; cellRef = factory.createCharArray("Albrecht Durer's Melancolia with a magic square" ); outputs[0] = imageCell; } void checkArguments(ArgumentList inputs) { std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine(); if (inputs[0].getType() != ArrayType::CELL) { matlabPtr->feval(u"error", 0, std::vector<Array> ({ factory.createScalar("Input must be a cell array") })); } } };
Сохраните этот код в файле с именем modifyCellArray.cpp
, создайте файл MEX и вызовите функцию.
mex modifyCellArray.cpp
durerCell = modifyCellArray(durerCell);
Возвращенный массив ячеек содержит новый символьный массив, присвоенный в MEX-функции.
durerCell{2}
ans = 'Albrecht Durer's Melancolia with a magic square'
Для получения дополнительной информации о работе с массивами ячеек смотрите C++ Массивы ячеек.
MATLAB Data API задает matlab::data::StructArray
, чтобы представлять MATLAB массивы struct
. Каждое поле в самом StructArray
содержит массив. Поэтому можно изменить поле StructArray
, переданного MEX-функции, не заставляя целый массив быть скопированными. Поля, не измененные, остаются разделяемыми с полями входного массива.
Класс StructArray
обеспечивает функции членства, чтобы получить доступ к полям структуры:
getFieldNames
возвращает начальные и конечные итераторы, обеспечивающие доступ к именам полей.
getNumberOfFields
возвращает количество полей в структуре.
Например, можно сгенерировать вектор имен полей и использовать эти имена, чтобы получить доступ к данным в структуре. Этот код принимает, что поле содержит массив типа double
.
auto fields = myStruct.getFieldNames();
std::vector<matlab::data::MATLABFieldIdentifier> fieldNames(fields.begin(), fields.end());
// Get the data from one field
matlab::data::TypedArray<double> field1 = myStruct[0][fieldNames[0]]
Присвойте новые значения полю путем создания массива правильного типа.
myStruct[0][fieldNames[0]] = factory.createArray<double>({ 1,5 }, { 1, 2, 3, 4, 5 });
Создайте ссылку на поле структуры с помощью matlab::data::Reference<T>
, который можно передать функциям, которые получают доступ к значениям в поле. Ссылки позволяют вам получить значение поля, присвоить новое значение полю и создать другой массив, который относится к тому же значению. Например, этот код создает ссылку на поле, содержащее массив, удваивается.
auto fields = myStruct.getFieldNames(); std::vector<matlab::data::MATLABFieldIdentifier> fieldNames(fields.begin(), fields.end()); matlab::data::TypedArrayRef<double> field1Reference = myStruct[0][fieldNames[0]]
Присвойте новые значения полю с помощью этой ссылки.
field1Reference = factory.createArray<double>({ 1,5 }, { 1, 2, 3, 4, 5 });
Этот пример передает структуру MATLAB MEX-функции. Структура содержит два больших поля данных и два поля, чтобы содержать скалярные значения, присвоенные MEX-функцией. MEX-функция вычисляет среднее значение для чисел в каждом массиве и присваивает эти значения полю Average
соответствующей структуры.
Этот код MATLAB создает массив структур и передает его MEX-функции, созданной из файла modifyStruct.cpp
, описанного сюда.
s = struct('Average',{[],[]},... 'Data',{rand(1,1000),randi([1,9],1,1000)}); s = modifyStruct(s);
Вот функция MexFunction::operator()
. Это выполняет эти операции:
Вызовите функцию checkArgument
, чтобы проверять вводы и выводы на размер и тип.
Присвойте вход переменной matlab::data::StructArray
.
Вызовите функцию calcMean
, чтобы вычислить средние значения.
Присвойте обновленный массив структур MEX-функции вывод.
#include "mex.hpp" #include "mexAdapter.hpp" using matlab::mex::ArgumentList; using namespace matlab::data; class MexFunction : public matlab::mex::Function { public: void operator()(ArgumentList outputs, ArgumentList inputs) { checkArguments(outputs, inputs); StructArray inStruct(inputs[0]); calcMean(inStruct); outputs[0] = inStruct; }
Функция checkArguments
выполняет эти проверки:
Количество входных параметров равняется тому.
Количество выходных параметров не больше, чем один.
Вход является массивом структур MATLAB.
void checkArguments(ArgumentList outputs, ArgumentList inputs) { std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr = getEngine(); ArrayFactory factory; if (inputs.size() != 1) { matlabPtr->feval(u"error", 0, std::vector<Array>({ factory.createScalar("One input required") })); } if (outputs.size() > 1) { matlabPtr->feval(u"error", 0, std::vector<Array>({ factory.createScalar("Too many outputs specified") })); } if (inputs[0].getType() != ArrayType::STRUCT) { matlabPtr->feval(u"error", 0, std::vector<Array>({ factory.createScalar("Input must be a structure") })); } }
Функция calcMean
вычисляет среднее значение для каждого набора чисел в полях Data
и присваивает эти значения полю Average
соответствующей структуры.
void calcMean(StructArray& inStruct) { ArrayFactory factory; auto fields = inStruct.getFieldNames(); std::vector<MATLABFieldIdentifier> fieldNames(fields.begin(), fields.end()); double sm = 0; for (auto i = 0; i < 2; i++) { const TypedArray<double> data = inStruct[i][fieldNames[1]]; for (auto& elem : data) { sm += elem; } inStruct[i][fieldNames[0]] = factory.createScalar(sm / data.getNumberOfElements()); } } };
Для связанного примера, который использует ячейку и массивы структур, откройте этот исходный файл в редакторе MATLAB phonebook.cpp
.
matlab::data::MATLABFieldIdentifier