Main Content

型付き配列、cell 配列および構造体配列のデータ アクセス

C++ MEX 関数で使用される MATLAB® データ API は、MATLAB で直接作成された関数で使用されているものと同じコピーオンライト セマンティクスを提供します。MEX 関数は渡されたデータ配列を複製できますが、この複製は元の変数の共有コピーです。MEX 関数によって配列の値が変更されると、データの独立したコピーが作成されます。

MEX 関数の呼び出しに際して、matlab::data::Array の入力は、MEX 関数に渡される MATLAB 変数の共有コピーです。共有コピーは、次のような MEX 関数の場合に利点を提供します。

  • MEX 関数に渡される大きな配列を変更しない場合。

  • 入力として渡される構造体全体のコピーを作成せずに、構造体の特定のフィールドを変更する場合。

  • 入力として渡される cell 配列のディープ コピーを作成せずに、cell 配列の cell を変更する場合。

  • 入力として渡されるオブジェクトのディープ コピーを作成せずに、オブジェクトのプロパティを変更する場合。

共有コピー

次のコード例は、イメージ配列の平均値を計算する際に共有コピーを使用するという利点があります。入力配列を、反復子をサポートする 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 関数の cell 配列の変更

MEX 関数に cell 配列を渡す際、その MATLAB データ API 表現 matlab::data::CellArray は、本質において各 cell に matlab::data::Array が格納された matlab::data::Array です。この設計により、格納された各配列を、変更されるまで共有コピーにしておくことができます。

MEX 関数で cell 配列の cell を変更すると、cell 配列全体ではなく、該当する cell に格納された配列のみが共有解除されます。たとえば、cell 配列 A を cell 配列 B にコピーするとします。各 cell の値は共有されます。

Each cell in array A maps to a cell in array B.

cell 配列 B で Cell1 を変更した場合、その cell の配列は cell 配列 A の Cell1 と共有されなくなります。ただし、cell 配列全体をコピーする必要が生じないように、その他すべての cell の値は共有されたままになっています。

Each cell except Cell1 in array A maps to a cell in array B.

この例では、cell 配列を MEX 関数に渡し、他の cell のデータをコピーせずに、cell 配列の 1 つの cell を変更する方法を示します。この例はイメージ データを MATLAB ワークスペースに読み込み、イメージのデータ、キャプション、カラーマップが格納された cell 配列を作成します。

load durer
whos
  Name           Size               Bytes  Class 

  X            648x509            2638656  double
  caption        2x28                 112  char  
  map          128x3                 3072  double 

各変数を異なる cell に代入します。

durerCell{1} = X;
durerCell{2} = caption;
durerCell{3} = map;

この MEX 関数は std::move を使用して入力 cell 配列を matlab::data::CellArray に移動します。これは、個々のセルへのアクセスを提供します。変更するデータは、cell 配列の 1 つの cell に格納されたキャプションだけです。この cell を変更するには、次を行います。

#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);

返される cell 配列には、MEX 関数に代入された新しい文字配列が格納されています。

durerCell{2}
ans =

    'Albrecht Durer's Melancolia with a magic square'

cell 配列での処理の詳細は、C++ cell 配列を参照してください。

MEX 関数の構造体の変更

MATLAB データ API では、MATLAB 配列 struct を表すための matlab::data::StructArray を定義しています。StructArray の各フィールドそのものに、配列が格納されています。したがって、配列全体をコピーせずに、MEX 関数に渡される StructArray のフィールドを変更できます。変更していないフィールドは、引き続き入力配列のフィールドと共有されます。

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 });

構造体のフィールドへの代入

この例では、MEX 関数に MATLAB 構造体を渡します。構造体には 2 つの大きなデータ フィールドと、MEX 関数によって代入されたスカラー値が格納される 2 つのフィールドがあります。MEX 関数は、各配列の数値の平均値を計算し、これらの値を、対応する構造体の Average フィールドに代入します。

この MATLAB コードは、構造体配列を作成し、ここで説明する modifyStruct.cpp ファイルからビルドされた MEX 関数に渡します。

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 は次のチェックを実行します。

  • 入力の数が 1 に等しい。

  • 出力の数が 1 より大きくない。

  • 入力は、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") }));
        }
    }

関数 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());
        }
    }
};

cell 配列と構造体配列の例

関連する cell 配列と構造体配列の使用例については、ソース ファイル phonebook.cpp を MATLAB エディターで開きます。

参考

関連するトピック