型付き配列、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 の値は共有されます。
cell 配列 B で Cell1
を変更した場合、その cell の配列は cell 配列 A の Cell1
と共有されなくなります。ただし、cell 配列全体をコピーする必要が生じないように、その他すべての cell の値は共有されたままになっています。
この例では、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 を変更するには、次を行います。
キャプションが格納された cell 内の配列への
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);
返される 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 エディターで開きます。
参考
matlab::data::MATLABFieldIdentifier