Wolfram LibraryLink を使うと,ダイナミックライブラリをWolfram言語カーネルに直接ロードして,ライブラリの関数を即座にWolfram言語内部から呼び出せるようにすることができる.整数,実数,パックアレー,文字列等のCのようなデータ型だけでなく,任意のWolfram言語式も交換することができる.また,エラーを送ったり,Wolfram言語にコールバックしたりするような便利な関数もある.

ドキュメントにはWolfram Librariesの例がたくさん含まれている.これらにはWolfram言語からライブラリを呼び出す際のさまざまな側面を例示する多くの短い関数が含まれている.

例を使う

ドキュメントにはサンプルライブラリのソースが含まれている.ライブラリの構築にはCコンパイラへのアクセスが必要となる.CCompilerDriverパッケージが役立つかもしれない.

使用中のプラットフォームでデモサンプルが見付かったことを示している.

demo例題ライブラリから関数をロードする.

関数を呼び出す.

ソース

例題のソースはドキュメントパクレットにある.これは次の入力を評価すると見付かる.

demo.c基本例題のサンプル
demo_shared.c共有渡しの基本例題のサンプル
demo_error.cエラーキャッチのサンプル
demo_string.c文字列引数と結果を使ったサンプル
demo_LinkObject.c引数と結果指定にLinkObjectを使ったサンプル
demo_managed.cxxCreateManagedLibraryExpressionを使ったサンプル
demo_callback.cライブラリからCompiledFunctionをコールバックするサンプル
demo_sparse.cSparseArrayを使ったサンプル
demo_image.cxxImageを使ったサンプル
demo_numericarray.cxxNumericArrayByteArray使ったサンプル

ライブラリ例題ソースファイル

demo

demoの例題には多くの関数が含まれている.以下はそれを使う例である

demo_shared

demo_sharedの例題にはパックアレーをライブラリ関数と共有する例題が含まれている.以下で多数の関数をロードする.

パックアレーを作成し,ライブラリにロードする.共有メモリ渡しが使われるため,配列は他の関数呼出しで使える.

10番目の要素である10を取得する.

配列をアンロードする.その後はこの配列は使えなくなる.

いくつかの関数のソースである.

DLLEXPORT int loadArray(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {

    tensor = MArgument_getMTensor(Args[0]);
    MArgument_setInteger(Res, 0);
    return LIBRARY_NO_ERROR;
}

DLLEXPORT int getElementVector(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {
    mint pos;
    double value;
    
    pos = MArgument_getInteger(Args[0]);
    value = libData->MTensor_getReal( tensor, pos);
    
    MArgument_setReal(Res, value);
    return LIBRARY_NO_ERROR;
}

DLLEXPORT int unloadArray(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {

    libData->MTensor_disown( tensor);
    MArgument_setInteger(Res, 0);
    return LIBRARY_NO_ERROR;
}

demo_error

demo_errorの例題はエラーを引き起こす関数呼出しの例を含む.ライブラリから関数をロードする.

errordemo1関数のソースである.MTensor引数を取り,実数データの取得を試みる.

DLLEXPORT int errordemo1(WolframLibraryData libData,
            mint Argc, MArgument *Args, MArgument Res) {
    MTensor T0, T1;
    mint I0, I1, res;
    mint pos[2];
    double *data;

    T0 = MArgument_getMTensor(Args[0]);
    I0 = MArgument_getInteger(Args[1]);
    
    data = libData->MTensor_getRealData(T0);
    MArgument_setReal(Res, data[I0]);
    return LIBRARY_NO_ERROR;
}

例題は整数MTensorを取る.関数はMTensor_getRealDataを呼び出すがこれがエラーとなり,LibraryFunctionError式を返す.

demo_string

demo_stringの例題は要求されたメモリ管理で文字列の引数と結果を使う方法を示す.ASCII文字列に対して簡単なシフト暗号化を行う関数をロードする.

以下がエンコーディング関数のソースである.引数はエンコードする文字列で,整数シフトが適用される.

DLLEXPORT int reverseString( WolframLibraryData libData, MLINK mlp)
{
    int res = LIBRARY_FUNCTION_ERROR;
    long len;
    const char *inStr = NULL;
    char* outStr = NULL;
    
    if ( !MLTestHead( mlp, "List", &len))
        goto retPt;
    if ( len != 1)
        goto retPt;

    if(! MLGetString(mlp, &inStr))
        goto retPt;

    if ( ! MLNewPacket(mlp) )
        goto retPt;

    outStr = reverseStringImpl(inStr);
    
    if (!MLPutString( mlp,outStr))
        goto retPt;
    res = 0;
retPt:
    if ( inStr != NULL)
        MLReleaseString(mlp, inStr);
    if ( outStr != NULL)
        free( (void*) outStr);
    return res;
}

これが例題である.

文字列参照は関数外のスコープの変数に保管される.この参照は関数が再び呼ばれ(そして新しい文字列が参照され)るか,(関数WolframLibrary_uninitializeの中で)ライブラリがアンロードされるかすると解放される.文字列引数のメモリはライブラリ関数がすべて所有するので,メモリ内でインプレースエンコーディングが行われ,別の文字列への割当てを必要としない.

demo_LinkObject

demo_LinkObjectの例題は,引数と結果の指定としてLinkObjectを使う例を示す.ライブラリから関数をロードする.

以下はreverseString関数のソースである.WSLINK引数を取り,Wolfram Symbolic Transfer Protocol (WSTP) APIを使ってリスト中の引数を読み取る.結果は生成されるとリンク上に書き出される.

DLLEXPORT int reverseString( WolframLibraryData libData, WSLINK mlp)
{
    int res = LIBRARY_FUNCTION_ERROR;
    long len;
    const char *inStr = NULL;
    char* outStr = NULL;
    
    if ( !WSTestHead( mlp, "List", &len))
        goto retPt;
    if ( len != 1)
        goto retPt;

    if(! WSGetString(mlp, &inStr))
        goto retPt;

    if ( ! WSNewPacket(mlp) )
        goto retPt;

    outStr = reverseStringImpl(inStr);
    
    if (!WSPutString( mlp,outStr))
        goto retPt;
    res = 0;
retPt:
    if ( inStr != NULL)
        WSReleaseString(mlp, inStr);
    if ( outStr != NULL)
        free( (void*) outStr);
    return res;
}

文字列を渡す,結果がリンクから読み取られ,表示される.

demo_managed

demo_managedの例題は,Wolfram言語式に関連付けられたクラスやオブジェクトのインスタンスを別々に保存するために,管理されたライブラリ式をどのように使えばよいかを示すものである.

この例題の関数は,異なる状態(またはパラメータ)を同時に許可する,非常に簡単であまり不規則ではない(あるいは,特に効率的に実装された)線形合同法を実装する.

コードの重要な部分は,registerLibraryExpressionManagerunregisterLibraryExpressionManagerが使われている初期化関数と初期化解除関数にある.

/* ライブラリの初期化 */
EXTERN_C DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData)
{
    return (*libData->registerLibraryExpressionManager)("LCG", manage_instance);
}

/* ライブラリの初期化解除 */
EXTERN_C DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData)
{
    int err = (*libData->unregisterLibraryExpressionManager)("LCG");
}

ファイルdemo_managed.cxxのコードは,hashmapクラスについてのみC++を使っている.これはIDをインスタンスにマップするのに便利である.関数manage_instanceは,モードが0の場合(CreateManagedLibraryExpressionが使われている場合)はIDをhashmapに加え,モードが1の場合はMTensorを解放してhashmapからIDを消去するよう定義されている.

DLLEXPORT void manage_instance(WolframLibraryData libData, mbool mode, mint id)
{
    if (mode == 0) {
        MTensor *T = new(MTensor);
        map[id] = T;
        *T = 0;
    } else {
        MTensor *T = map[id];
        if (T != 0) {
            if (*T != 0) (*libData->MTensor_free)(*T);
            map.erase(id);
        }
    }
}

以下のようにしてライブラリをロードし(名前「LCG」を持つマネージャのレジストレーションはライブラリが最初にロードされたときに実行される),インスタンスを操作するLibraryFunctionsをいくつか定義する.

次で,インスタンスのハンドルとして使われる頭部LCGを持つ式の型を設定するための定義をいくつか作成する.

次で乱数生成器を定義する.

以下はインスタンスを設定(パラメータは「Numerical Recipes (1992)」のものを使用),テストし,そのインスタンスから数を生成する.

別のインスタンスを設定し,そのインスタンスから2つの数を生成する.IDが一意的であることに注目のこと.

すべてのインスタンスを状態とともに表示する.

もう1つ数を生成した後,最初と2つ目のインスタンスは同じ状態になる.

次で3つ目のインスタンスを設定するが,割当てが確実にインスタンス式への唯一の参照になり,行列を生成するためにそれを使うように注意する.

以下で2つ目のインスタンスを解放する.

g2 の値の設定を解除すると,3つ目のインスタンスへの参照すべてがなくなり,manage_instance関数が自動的に呼び出され,hashmapからインスタンスを削除する.

ライブラリがアンロードされると,残りのインスタンスは削除されgは管理されたライブラリ式ではなくなる.

demo_callback

demo_callback例題は,Wolfram言語でライブラリからCompiledFunctionへコールバックする方法を示す.

コードで重要な部分は,registerLibraryCallbackManagerunregisterLibraryCallbackManagerが使われている初期化関数と初期化解除関数にある.

/* ライブラリの初期化 */
DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData)
{
    call_id = 0;
    call_nargs = 0;
    return (*libData->registerLibraryCallbackManager)("demo", manage_callback);
}

/* ライブラリの初期化解除 */
DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData)
{
    (*libData->unregisterLibraryCallbackManager)("demo");
}

コールバックマネージャ関数manage_callbackは,この例題では非常に単純であり,一度に一つだけの接続関数を許可する.関数が呼び出されたとき,正のID(システムによって生成されるIDはすべて正である)がすでに存在するならば,そのIDに関連付けられた関数が解放され,新しいIDが保存される.複数のIDを保存するためにもっと高度なコードを使うと,一度に複数の関数を接続することができる.

DLLEXPORT mbool manage_callback(WolframLibraryData libData, mint id, MTensor argtypes)
{
    mint i;
    mint *dims;
    mint *typerank;
    if (call_id) {
        (*libData->releaseLibraryCallbackFunction)(call_id);
        call_id = 0;
        free(cbArgs);
        free(tdata);
    }
    call_id = id;
    dims = (*libData->MTensor_getDimensions)(argtypes);
    call_nargs = dims[0] - 1;
    if (call_nargs == 0) {
        call_id = 0;
        return False;
    }
    typerank = (*libData->MTensor_getIntegerData)(argtypes);
    /* 引数と結果(i <= call_nargs ループ制御)がスカラーのmrealかどうかをチェックする */
    for (i = 0; i <= call_nargs; i++) {
        /* 各行は{type, rank} */
        if ((typerank[0] != MType_Real) || (typerank[1] != 0)) {
            call_id = 0;
            call_nargs = 0;
            return False;
        }
        typerank += 2;
    }
    cbArgs = (MArgument *) malloc((call_nargs + 1)*sizeof(MArgument));
    tdata = (mreal **) malloc(call_nargs*sizeof(mreal *));
    return True;
}

次でライブラリをロードし(名前"demo_callback_manager"を含むマネージャのレジストレーションは,ライブラリが最初にロードされたときに実行される),型mrealの配列の各要素に対するコールバック関数を呼び出すLibraryFunctionを定義する.

以下は正弦関数をコンパイルし,CompiledFunctionをライブラリに接続する.

以下は乱数実数の配列にLibraryFunctionを呼び出す.

結果は直接Sinを評価するのと同じである.

コールバックを使う必要時間を比較するために,標準的なC sin()関数を直接使う関数が定義される.

値は等しい.

もちろん,Wolfram言語でSin関数を使うと,並列評価になる可能性があるので速くなる.

この場合,並列評価で標準のCライブラリとは異なる関数が使用されるため,わずかな差がある.

CompilationTarget->"C"を使って関数をCにコンパイルすると,オーバーヘッドはかなり小さくなる.

このメカニズムはCompiledFunctionがWolfram言語評価子を使う必要があるときでも動作する.

しかし,この場合の関数は,最コンパイルなしで修正される可能性がある.

管理関数は,CompiledFunctionの結果と引数がすべてスカラーの実数でない場合接続を拒否する.そのためConnectLibraryCallbackFunctionFalseを返す.

コールバックを適用しようとするとエラーが出る.

以下のコードは複数の引数を許可する.次はロジスティック写像 を1000回繰り返す例である.

これを使うためには,2つの引数を使うLibraryFunctionオーバーロードが必要になる.

demo_sparse

demo_sparse例題は,Wolfram言語でMSparseArrayの引数と結果をSparseArrayオブジェクトと使う方法を示す.

デモの関数sparse_propertiesにより圧縮行格納(CSR)データ構造がどのように作用するかを調べることが可能になる.次でライブラリからこの関数をロードする.

これはsparse_properties関数のソースである.引数は希望のプロパティを見付けるための文字列とMSparseArrayであり,どのような型,階数でもよい.この関数はMSparseArray引数の読込みアクセスのみを使うので,"Constant"渡しの使用は妥当である.

DLLEXPORT int sparse_properties( WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) 
{
    int err = LIBRARY_NO_ERROR;
    char *what;
    mint *data;
    MSparseArray S;
    MTensor *T, Tres = 0;
    WolframSparseLibrary_Functions sparseFuns = libData->sparseLibraryFunctions;

    if (Argc != 2) return LIBRARY_FUNCTION_ERROR;
        
    what = MArgument_getUTF8String(Args[0]);
    S = MArgument_getMSparseArray(Args[1]);

    if (!strcmp(what, "ImplicitValue")) {
        T = (*(sparseFuns->MSparseArray_getImplicitValue))(S);
    } else if (!strcmp(what, "ExplicitValues")) {
        T = (*(sparseFuns->MSparseArray_getExplicitValues))(S);
    } else if (!strcmp(what, "RowPointers")) {
        T = (*(sparseFuns->MSparseArray_getRowPointers))(S);
    } else if (!strcmp(what, "ColumnIndices")) {
        T = (*(sparseFuns->MSparseArray_getColumnIndices))(S);
    } else if (!strcmp(what, "ExplicitPositions")) {
        err = (*(sparseFuns->MSparseArray_getExplicitPositions))(S, &Tres);
    } else if (!strcmp(what, "Normal")) {
        err = (*(sparseFuns->MSparseArray_toMTensor))(S, &Tres);
    } else {
        err = LIBRARY_FUNCTION_ERROR;
    }
    if (err) return err;
    if (!Tres) (*(libData->MTensor_clone))(*T, &Tres);

    MArgument_setMTensor(Res, Tres);
    return err;
}

ソースで特筆すべき点は,MSparseArray_getImplicitValueMSparseArray_getImplicitValueMSparseArray_getColumnIndicesMSparseArray_getRowPointersによりCSRデータに対して返されたMTensor参照はMSparseArrayに属しているため,それらを返すためには,MSparseArrayデータののMTensorsが故意でなく解放されることはないということである.

次は,さまざまな疎配列に対する特性のいくつかを示す例である.

以下はCSRデータ構造の表を作成し,明示的な位置と通常の配列も示す.

暗示的な値,あるいはいずれかの値が機械精度を持つ場合,値はすべてSparseArrayからMSparseArrayへの変換の強制段階に強制される.

混合型のSparseArrayにWolfram言語のNormalを使うと,厳密値が維持されるが,パックアレーではない.

疎ベクトルはCSRでは1行の行列として表される.

階数4の配列は,要素につき3つの列指標を持つ.

以下でその場でSparseArrayの値を変更することができる関数をロードする.

以下はsparse_modify_values関数のソースである.

DLLEXPORT int sparse_modify_values( WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res)
{
    char *what;
    int err = 0;
    mbool resparsify;
    mint i, nz;
    mreal *t, *v;
    MSparseArray S = 0, Sout = 0;
    MTensor T = 0, *Vp = 0;
    WolframSparseLibrary_Functions sparseFuns = libData->sparseLibraryFunctions;

    if (Argc != 2) return LIBRARY_FUNCTION_ERROR;

    S = MArgument_getMSparseArray(Args[0]);
    Vp = (*sparseFuns->MSparseArray_getExplicitValues)(S);
    if ((*libData->MTensor_getType)(*Vp) != MType_Real) return LIBRARY_TYPE_ERROR;
    nz = (*libData->MTensor_getFlattenedLength)(*Vp);
    v = (*libData->MTensor_getRealData)(*Vp);

    T = MArgument_getMTensor(Args[1]);
    if ((*libData->MTensor_getType)(T) != MType_Real) return LIBRARY_TYPE_ERROR;
    if ((*libData->MTensor_getFlattenedLength)(T) != nz) return LIBRARY_DIMENSION_ERROR;
    t = (*libData->MTensor_getRealData)(T);

    for (i = 0; i < nz; i++) v[i] = t[i];

    /* 明示的な位置を再計算する */
    err = (*sparseFuns->MSparseArray_resetImplicitValue)(S, NULL, &Sout);

    (*sparseFuns->MSparseArray_disown)(S);

    if (!err)
        MArgument_setMSparseArray(Res, Sout);

    return err;
}

返される疎配列は明示的な位置を再計算することで生成される.これはSparseArray[s]sSparseArray)を使った場合に起こることとほぼ同じである.値をその場で変更するために,データは,MSparseArrayにより所有されている値に対する既存のMTensorにコピーされる. カーネルのSparseArrayに影響が及ぶためには,渡すことが"Shared"である必要がある.

新しい値の一つが0だったので,その位置は明示的に表現される必要はなく,s1 の構造はもっとコンパクトになる.

demo_image

demo_imageの例題はWolfram言語のImageImage3Dで,MImageの引数と結果を使う方法を示す.

デモの関数color_negateは,すべての色が反転した負のImageまたはImage3Dオブジェクトを取得する方法を示す.ライブラリからこの関数をロードする.

以下がcolor_negate関数のソースである.引数は反転されるMImageである.

このコードは画像に存在するすべての画素を調べ,それらを反転する.これが最速の実装である.しかし,アルファチャンネルも反転されるため,アルファチャンネルのある画像を正しく反転させることはできない.


template <typename T> static T maxValue() {
return -1; // ERROR
}

template <> char maxValue<char>() { return 1; }

template <> raw_t_ubit8 maxValue<raw_t_ubit8>() { return 255; }

template <> raw_t_ubit16 maxValue<raw_t_ubit16>() { return 65535; }

template <> raw_t_real32 maxValue<raw_t_real32>() { return 1.0f; }

template <> raw_t_real64 maxValue<raw_t_real64>() { return 1.0; }

template <typename T>
static void icolor_negate(void *out0, const void *in0, mint length) {
mint ii;
T *out = reinterpret_cast<T *>(out0);
const T *in = reinterpret_cast<const T *>(in0);
for (ii = 0; ii < length; ii++) {
out[ii] = maxValue<T>() - in[ii];
}
}

/* 画像の色の反転 */
EXTERN_C DLLEXPORT int color_negate(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument res) {
mint length;
MImage image_in, image_out = 0;
void *data_in, *data_out;
int err = LIBRARY_FUNCTION_ERROR;
imagedata_t type;
WolframImageLibrary_Functions imgFuns = libData->imageLibraryFunctions;

if (Argc < 1) {
return err;
}

image_in = MArgument_getMImage(Args[0]);

err = imgFuns->MImage_clone(image_in, &image_out);
if (err)
return err;

type = imgFuns->MImage_getDataType(image_in);
length = imgFuns->MImage_getFlattenedLength(image_in);

data_in = imgFuns->MImage_getRawData(image_in);
data_out = imgFuns->MImage_getRawData(image_out);
if (data_in == NULL || data_out == NULL)
goto cleanup;

switch (type) {
case MImage_Type_Bit:
icolor_negate<char>(data_out, data_in, length);
break;
case MImage_Type_Bit8:
icolor_negate<raw_t_ubit8>(data_out, data_in, length);
break;
case MImage_Type_Bit16:
icolor_negate<raw_t_ubit16>(data_out, data_in, length);
break;
case MImage_Type_Real32:
icolor_negate<raw_t_real32>(data_out, data_in, length);
break;
case MImage_Type_Real:
icolor_negate<raw_t_real64>(data_out, data_in, length);
break;
default:
goto cleanup;
}

MArgument_setMImage(res, image_out);
return err;

cleanup:
imgFuns->MImage_free(image_out);
return err;
}

以下は,画像の色を反転させる方法を示した例題である.

次の例題は,RGB画像をグレースケールに変換する方法を示す.関数rgb_to_grayをロードする.

以下がrgb_to_gray関数のソースである.

template <typename T>
static void irgb_to_gray(void *out0, const void *in0, mint rows, mint cols,
mbool alphaQ) {
mint row;
mint col;
T r, g, b;

T *out = reinterpret_cast<T *>(out0);
const T *in = reinterpret_cast<const T *>(in0);

if (alphaQ) {
for (row = 0; row < rows; row++) {
for (col = 0; col < cols; col++) {
mint idx = row * cols + col;
r = in[4 * idx];
g = in[4 * idx + 1];
b = in[4 * idx + 2];
out[2 * idx] = (T)(.299 * r + .587 * g + .114 * b);
out[2 * idx + 1] = in[4 * idx + 3];
}
}
} else {
for (row = 0; row < rows; row++) {
for (col = 0; col < cols; col++) {
mint idx = row * cols + col;
r = in[3 * idx];
g = in[3 * idx + 1];
b = in[3 * idx + 2];
out[idx] = (T)(.299 * r + .587 * g + .114 * b);
}
}
}
}

/* RGB画像からグレースケールへの変換 */
EXTERN_C DLLEXPORT int rgb_to_gray(WolframLibraryData libData, mint Argc,
MArgument *Args, MArgument res) {
mint rows, columns;
mbool alphaQ;
int err = 0;
imagedata_t type;
MImage image_in, image_out;
void *data_in, *data_out;
WolframImageLibrary_Functions imgFuns = libData->imageLibraryFunctions;

if (Argc < 1) {
return LIBRARY_FUNCTION_ERROR;
}

image_in = MArgument_getMImage(Args[0]);
if (imgFuns->MImage_getColorSpace(image_in) != MImage_CS_RGB)
return LIBRARY_FUNCTION_ERROR;

/*This function accepts only 2D images, but can be easily extended to work
* with Image3D.*/
if (imgFuns->MImage_getRank(image_in) == 3)
return LIBRARY_FUNCTION_ERROR;

type = imgFuns->MImage_getDataType(image_in);
rows = imgFuns->MImage_getRowCount(image_in);
columns = imgFuns->MImage_getColumnCount(image_in);
alphaQ = imgFuns->MImage_alphaChannelQ(image_in);

err = imgFuns->MImage_new2D(columns, rows, alphaQ ? 2 : 1, type,
MImage_CS_Gray, True, &image_out);
if (err)
return LIBRARY_FUNCTION_ERROR;

data_in = imgFuns->MImage_getRawData(image_in);
data_out = imgFuns->MImage_getRawData(image_out);
if (data_in == NULL || data_out == NULL)
return LIBRARY_FUNCTION_ERROR;

switch (type) {
case MImage_Type_Bit:
// RGB binary images are not allowed
imgFuns->MImage_free(image_out);
return LIBRARY_FUNCTION_ERROR;
case MImage_Type_Bit8:
irgb_to_gray<raw_t_ubit8>(data_out, data_in, rows, columns, alphaQ);
break;
case MImage_Type_Bit16:
irgb_to_gray<raw_t_ubit16>(data_out, data_in, rows, columns, alphaQ);
break;
case MImage_Type_Real32:
irgb_to_gray<raw_t_real32>(data_out, data_in, rows, columns, alphaQ);
break;
case MImage_Type_Real:
irgb_to_gray<raw_t_real64>(data_out, data_in, rows, columns, alphaQ);
break;
default:
imgFuns->MImage_free(image_out);
return LIBRARY_FUNCTION_ERROR;
}
MArgument_setMImage(res, image_out);
return LIBRARY_NO_ERROR;
}

この関数は2D画像のみを受け付けるが,Image3Dが使えるように簡単に拡張することができる.

MImage_getByteMImage_setByte等のヘルパー関数もCの指標計算を避けるために使用することができる.これらの関数はWolfram言語の部分と同じ番号付け方法を使っている.つまり,最初の要素は1となるのである.

demo_numericarray

demo_numericarrayの例題は,Wolfram言語でNumericArrayオブジェクトとByteArrayオブジェクトを使ってMNumericArrayの引数と結果を使う方法を示す.

デモの関数numericArrayReverseは,一次元NumericArrayオブジェクトの要素を反転させる方法を示す.ライブラリからこの関数をロードする.

以下はnumericArrayReverse関数のソースである.引数は反転させようとしているMNumericArrayである.このコードは入力配列内のすべての要素の順序を入れ替える.

template <typename T>
static void tplNumericArrayReverse(void *out0, const void *in0, mint length) {
    T *out = reinterpret_cast<T *>(out0);
    const T *in = reinterpret_cast<const T *>(in0);

    for (mint i = 0; i < length; i++) {
        out[length - i - 1] = in[i];
    }
}

/* Reverses elements in a one-dimensional NumericArray */
EXTERN_C DLLEXPORT int numericArrayReverse(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res) {
    mint length;
    MNumericArray na_in = NULL, na_out = NULL;
    void *data_in = NULL, *data_out = NULL;
    int err = LIBRARY_FUNCTION_ERROR;
    int rank = 0;
    numericarray_data_t type = MNumericArray_Type_Undef;
    WolframNumericArrayLibrary_Functions naFuns = libData->numericarrayLibraryFunctions;

    if (Argc < 1) {
        return err;
    }

    na_in = MArgument_getMNumericArray(Args[0]);

    err = naFuns->MNumericArray_clone(na_in, &na_out);
    if (err) {
        return err;
    }
    rank = naFuns->MNumericArray_getRank(na_in);
if(rank != 1) {
        return err;
    }    
    type = naFuns->MNumericArray_getType(na_in);
    length = naFuns->MNumericArray_getFlattenedLength(na_in);
    data_in = naFuns->MNumericArray_getData(na_in);
    data_out = naFuns->MNumericArray_getData(na_out);
    if (data_in == NULL || data_out == NULL) {
        goto cleanup;
    }

    switch (type) {
    case MNumericArray_Type_Bit8:
        tplNumericArrayReverse<std::int8_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_UBit8:
        tplNumericArrayReverse<std::uint8_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Bit16:
        tplNumericArrayReverse<std::int16_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_UBit16:
        tplNumericArrayReverse<std::uint16_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Bit32:
        tplNumericArrayReverse<std::int32_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_UBit32:
        tplNumericArrayReverse<std::uint32_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Bit64:
        tplNumericArrayReverse<std::int64_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_UBit64:
        tplNumericArrayReverse<std::uint64_t>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Real32:
        tplNumericArrayReverse<float>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Real64:
        tplNumericArrayReverse<double>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Complex_Real32:
        tplNumericArrayReverse<std::complex<float>>(data_out, data_in, length);
        break;
    case MNumericArray_Type_Complex_Real64:
        tplNumericArrayReverse<std::complex<double>>(data_out, data_in, length);
        break;
    default:
        goto cleanup;
    }

    MArgument_setMNumericArray(res, na_out);
    return err;

cleanup:
    naFuns->MNumericArray_free(na_out);
    return err;
}

NumericArrayの要素を反転する方法の例を示す.

NumericArrayに保存された要素の複素共役を計算する方法の例を示す.関数numericArrayComplexConjugateをロードする.

以下はnumericArrayComplexConjugate関数のソースである.

template <typename T>
static void tplNumericArrayComplexConjugate(void *inout0, mint length) {
    T *inout = reinterpret_cast<T *>(inout0);
    
    for (mint i = 0; i < length; i++) {
        inout[i] = std::conj(inout[i]);
    }
}

/* Computes the complex conjugate of each element in a NumericArray.
NumericArrays of non-complex types are converted to MNumericArray_Type_Complex_Real64 */
EXTERN_C DLLEXPORT int numericArrayComplexConjugate(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res) {
    mint length;
    MNumericArray na_in = NULL, na_out = NULL;
    void *data_out = NULL;
    int err = LIBRARY_FUNCTION_ERROR;
    numericarray_data_t type = MNumericArray_Type_Undef;
    WolframNumericArrayLibrary_Functions naFuns = libData->numericarrayLibraryFunctions;

    if (Argc < 1) {
        return err;
    }

    na_in = MArgument_getMNumericArray(Args[0]);
    type = naFuns->MNumericArray_getType(na_in);
    if(type != MNumericArray_Type_Complex_Real32 && type != MNumericArray_Type_Complex_Real64) {
        err = naFuns->MNumericArray_convertType(&na_out, na_in, MNumericArray_Type_Complex_Real64, MNumericArray_Convert_Coerce, 0);    
        if(na_out == NULL) {
            return err;
        }
    }
    else {
        err = naFuns->MNumericArray_clone(na_in, &na_out);
        if (err) {
            return err;
        }
        length = naFuns->MNumericArray_getFlattenedLength(na_out);
        data_out = naFuns->MNumericArray_getData(na_out);
        if (data_out == NULL) {
            goto cleanup;
        }

        switch (type) {
        case MNumericArray_Type_Complex_Real32:
            tplNumericArrayComplexConjugate<std::complex<float>>(data_out, length);
            break;
        case MNumericArray_Type_Complex_Real64:
            tplNumericArrayComplexConjugate<std::complex<double>>(data_out, length);
            break;
        default:
            goto cleanup;
        }
    }

    MArgument_setMNumericArray(res, na_out);
    return LIBRARY_NO_ERROR;

cleanup:
    naFuns->MNumericArray_free(na_out);
    return err;
}

この関数はすべての型の数値配列を取ることができる.非複素型の数値配列は"ComplexReal64"型に変換される.

デモの関数readBytesFromFileはファイルからデータを読み取り,ByteArrayオブジェクトに保存する方法を示している.ライブラリからこの関数をロードする.

以下はreadBytesFromFile関数のソースである.引数はファイル名で,"UTF8String"として渡される.このコードは指定のファイルからすべてのバイトを読み取り,それらをByteArrayとして返す.

static mint readBytes(char const* filename, std::uint8_t** bytes) {
    std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
    if (ifs) {
        std::ifstream::pos_type pos = ifs.tellg();
        *bytes = new std::uint8_t[pos];
        ifs.seekg(0, std::ios::beg);
        ifs.read(reinterpret_cast<char*>(*bytes), pos);
        return pos;
    }
    return 0;
}

/* Reads data from a file and returns it as a ByteArray */
EXTERN_C DLLEXPORT int readBytesFromFile(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument res) {
    mint length = 0;
    char *filename = NULL;
    MNumericArray barray = NULL;
    std::uint8_t *barray_data = NULL;
    std::uint8_t *bytes = NULL;
    int err = LIBRARY_FUNCTION_ERROR;
    WolframNumericArrayLibrary_Functions naFuns = libData->numericarrayLibraryFunctions;

    filename = MArgument_getUTF8String(Args[0]);
    length = readBytes(filename, &bytes);
    if (length > 0) {
        err = naFuns->MNumericArray_new(MNumericArray_Type_UBit8, 1, &length, &barray);
        if (err != 0) {
            goto cleanup;
        }
        barray_data = static_cast<std::uint8_t*>(naFuns->MNumericArray_getData(barray));
        std::memcpy(barray_data, bytes, length);
    }
    else {
        goto cleanup;
    }

cleanup:
    if (err == LIBRARY_NO_ERROR) {
        MArgument_setMNumericArray(res, barray);
    }
    delete[] bytes;
    libData->UTF8String_disown(filename);
    return err;
}

以下はファイルからバイトを読み取る方法を示す例である.