MATLAB® Data API, используемый функциями C++ MEX, обеспечивает ту же семантику копирования при записи, используемую функциями, написанными непосредственно в MATLAB. MEX-функция может дублировать переданный ей массив данных, но этот дубликат является общей копией исходной переменной. Отдельная копия данных создается, когда MEX-функция изменяет значения в массиве.
Когда вы вызываете MEX-функцию, matlab::data::Array
входы являются общими копиями переменного MATLAB, переданными в MEX-функцию. Общие копии обеспечивают преимущество, когда MEX-функция:
Не изменяет большой массив, переданный в MEX-функцию.
Изменяет определенные поля структуры, которая передается как вход, не создавая копию всей структуры.
Изменяет камеру в массиве ячеек, который передается как вход, не вызывая глубокой копии массива ячеек
Изменяет свойство объекта, которое передается как вход, не делая глубокой копии объекта.
Этот пример кода выигрывает от использования общих копий при вычислении среднего значения массива изображений. Копирование массива входа в const
matlab::data::TypedArray<T>
, который поддерживает итераторы, позволяет использовать основанную на диапазоне 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()); } };
Сохраните исходный код MEX-функции в файле с именем aveImage.cpp
. Можно использовать эту функцию, чтобы найти среднее значение массива топографических данных. Загрузите topo.mat
файл в рабочее пространство MATLAB и передайте массив данных в MEX-функцию.
mex aveImage.cpp load topo d = uint8(topo); % convert data to use aveImage m = aveImage(d)
m = 73.5487
Когда вы передаете массив ячеек в MEX-функцию, его представление MATLAB Data API, matlab::data::CellArray
, является по существу matlab::data::Array
который содержит matlab::data::Array
в каждой камере. Этот проект позволяет каждому содержащемуся в массиве быть общей копией до изменения.
Когда вы изменяете камеру массива ячеек в MEX-функция, только массив, содержащийся в этой камере, не разделяется, а не весь массив ячеек. Например, предположим, что вы копируете массив ячеек A в массив ячеек B. Значение каждой камеры является общим.
Если вы изменяете Cell1
в массиве ячеек B массив в этой камере больше не является общим с Cell1
в массиве ячеек А. Однако значения всех других камер остаются общими, так что весь массив ячеек не должен быть скопирован.
Этот пример показывает, как передать массив ячеек в 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 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-функцию, не вызывая копирования всего массива. Поля, не измененные, остаются общими с полями входного массива.
The 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>
, который можно передать функциям, которые получают доступ к значениям в поле. Ссылки позволяют вам получить значение поля, назначить новое значение полю и создать другой массив, который ссылается на то же значение. Для примера этот код создает ссылку на поле, содержащий массив типа double.
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; }
The 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 structure") })); } }
The 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