Доступ к данным во введенном, ячейке и массивах структур

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 workspace и передайте массив данных MEX-функции. Сохраните исходный код MEX-функции в файле под названием aveImage.cpp.

mex aveImage.cpp
load topo
m = aveImage(topo)

m =

     -1.8948e+03

Измените массив ячеек в MEX-функции

Когда вы передаете массив ячеек MEX-функции, представлением MATLAB Data API его, matlab::data::CellArray, является по существу matlab::data::Array, который содержит matlab::data::Array в каждой ячейке. Этот дизайн позволяет каждому содержавшему массиву быть разделяемой копией, пока не изменено.

Когда вы изменяете ячейку массива ячеек в MEX-функции, только массив, содержавшийся в той ячейке, не разделен, не целый массив ячеек. Например, предположите, что вы копируете массив ячеек в массив ячеек B. Значение каждой ячейки совместно используется.

Если вы изменяете Cell1 в массиве ячеек B, массив в той ячейке больше не совместно используется с Cell1 в массиве ячеек A. Однако, значения всех других ячеек остаются разделяемыми так, чтобы целый массив ячеек не должен был быть скопирован.

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

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++ Массивы ячеек.

Измените структуру в MEX-функции

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.

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

Похожие темы

Была ли эта тема полезной?