C スタイルの数値演算を取得する場合は、MEX 関数を使用して C 演算子を直接呼び出すことができます。定義により、それらは C データ型のように機能します。
この方法は、 gnovice のオーバーライドよりもはるかに手間がかかりますが、大規模なコードベースによりよく統合され、組み込み型の定義を変更するよりも安全であるため、完全を期すために言及する必要があると思います。
以下は、Matlab 配列に対して C の "+" 操作を実行する MEX ファイルです。C スタイルの動作が必要な演算子ごとに、これらのいずれかを作成します。
/* c_plus.c - MEX function: C-style (not Matlab-style) "+" operation */
#include "mex.h"
#include "matrix.h"
#include <stdio.h>
void mexFunction(
int nlhs, mxArray *plhs[],
int nrhs, const mxArray *prhs[]
)
{
mxArray *out;
/* In production code, input/output type and bounds checks would go here. */
const mxArray *a = prhs[0];
const mxArray *b = prhs[1];
int i, n;
int *a_int32, *b_int32, *out_int32;
short *a_int16, *b_int16, *out_int16;
mxClassID datatype = mxGetClassID(a);
int n_a = mxGetNumberOfElements(a);
int n_b = mxGetNumberOfElements(b);
int a_is_scalar = n_a == 1;
int b_is_scalar = n_b == 1;
n = n_a >= n_b ? n_a : n_b;
out = mxCreateNumericArray(mxGetNumberOfDimensions(a), mxGetDimensions(a),
datatype, mxIsComplex(a));
switch (datatype) {
case mxINT32_CLASS:
a_int32 = (int*) mxGetData(a);
b_int32 = (int*) mxGetData(b);
out_int32 = (int*) mxGetData(out);
for (i=0; i<n; i++) {
if (a_is_scalar) {
out_int32[i] = a_int32[i] + b_int32[i];
} else if (b_is_scalar) {
out_int32[i] = a_int32[i] + b_int32[0];
} else {
out_int32[i] = a_int32[i] + b_int32[i];
}
}
break;
case mxINT16_CLASS:
a_int16 = (short*) mxGetData(a);
b_int16 = (short*) mxGetData(b);
out_int16 = (short*) mxGetData(out);
for (i=0; i<n; i++) {
if (a_is_scalar) {
out_int16[i] = a_int16[0] + b_int16[i];
} else if (b_is_scalar) {
out_int16[i] = a_int16[i] + b_int16[0];
} else {
out_int16[i] = a_int16[i] + b_int16[i];
}
}
break;
/* Yes, you'd have to add a separate case for every numeric mxClassID... */
/* In C++ you could do it with a template. */
default:
mexErrMsgTxt("Unsupported array type");
break;
}
plhs[0] = out;
}
次に、Matlab コードから呼び出す方法を理解する必要があります。すべてのコードを記述している場合は、どこでも "a + b" の代わりに "c_plus(a, b)" を呼び出すことができます。または、フィールドに Matlab 数値配列を保持し、適切な C スタイル MEX 関数を呼び出す plus() およびその他の操作を定義する、@cnumeric などの独自の数値ラッパー クラスを作成することもできます。
classdef cnumeric
properties
x % the underlying Matlab numeric array
end
methods
function obj = cnumeric(x)
obj.x = x;
end
function out = plus(a,b)
[a,b] = promote(a, b); % for convenience, and to mimic Matlab implicit promotion
if ~isequal(class(a.x), class(b.x))
error('inputs must have same wrapped type');
end
out_x = c_plus(a.x, b.x);
out = cnumeric(out_x);
end
% You'd have to define the math operations that you want normal
% Matlab behavior on, too
function out = minus(a,b)
[a,b] = promote(a, b);
out = cnumeric(a.x - b.x);
end
function display(obj)
fprintf('%s = \ncnumeric: %s\n', inputname(1), num2str(obj.x));
end
function [a,b] = promote(a,b)
%PROMOTE Implicit promotion of numeric to cnumeric and doubles to int
if isnumeric(a); a = cnumeric(a); end
if isnumeric(b); b = cnumeric(b); end
if isinteger(a.x) && isa(b.x, 'double')
b.x = cast(b.x, class(a.x));
end
if isinteger(b.x) && isa(a.x, 'double')
a.x = cast(a.x, class(b.x));
end
end
end
end
次に、C スタイルの int 動作が必要な @cnumeric で数値をラップし、それらを使用して計算します。
>> cnumeric(int32(intmax))
ans =
cnumeric: 2147483647
>> cnumeric(int32(intmax)) - 1
ans =
cnumeric: 2147483646
>> cnumeric(int32(intmax)) + 1
ans =
cnumeric: -2147483648
>> cnumeric(int16(intmax('int16')))
ans =
cnumeric: 32767
>> cnumeric(int16(intmax('int16'))) + 1
ans =
cnumeric: -32768
プリミティブ @int32 型を壊すことから分離された、C スタイルのオーバーフロー動作があります。さらに、通常の数値を期待する他の関数に @cnumeric オブジェクトを渡すことができ、入力を多態的に処理する限り「機能」します。
パフォーマンス上の注意: これはオブジェクトであるため、+ はビルトインではなくメソッド ディスパッチの速度が遅くなります。大きな配列の呼び出しが少ない場合、実際の数値演算は C で行われるため、これは高速になります。小さな配列の呼び出しが多いと、メソッド呼び出しごとのオーバーヘッドが大きくなるため、速度が低下する可能性があります。