Wolfram言語とのインタラクション
Wolfram LibraryLink を使うと,ダイナミックライブラリをWolfram言語カーネルに直接ロードして,ライブラリの関数を即座にWolfram言語内部から呼び出せるようにすることができる.整数,実数,パックアレー,文字列等のCのようなデータ型だけでなく,任意のWolfram言語式も交換することができる.また,エラーを送ったり,Wolfram言語にコールバックしたりするような便利な関数もある.
このセクションでは,Wolfram Librariesを使うためにWolfram言語が提供する関数について述べる.
LibraryFunctionLoad | ライブラリから関数をロードする |
LibraryFunction | ライブラリからロードされた関数へのハンドルの表現 |
LibraryFunctionUnload | 以前ライブラリからロードされた関数をアンロードする |
LibraryUnload | 以前ライブラリからロードされた関数をすべてアンロードする |
LibraryFunctionInformation | LibraryFunctionについての情報を返す |
$LibraryPath | ライブラリの検索に使用されるパス |
FindLibrary | ライブラリを検索する |
LibraryLoad | 他のライブラリが必要とするライブラリをロードする |
LibraryFunctionLoadはライブラリから関数をロードし,LibraryFunctionを返す.
整数の引数でLibraryFunctionを呼び出す.
LibraryFunctionInformationはロード元のライブラリ,引数や戻り値型等のLibraryFunctionについての情報を返す.
ライブラリの指定
LibraryFunctionLoadの第1引数はロードするライブラリである.これ/Library/myLibrary.dylibのように絶対ファイル名で与えることができる.しかし,ライブラリにパスからの相対指定を渡す方がより便利なことがよくある.また,プラットフォームごとにライブラリの拡張子が異なるという問題もある.以下に規則をまとめる.
Wolfram言語はFindLibraryと$LibraryPathでこの問題を解決する.FindLibraryはプラットフォーム非依存であるライブラリ指定を取ることができ,それを$LibraryPath上で探して,ライブラリを見付けることができたらシステムの実際のファイルを返す.
次の例は,$LibraryPath上を検索して使用中のプラットフォームに適したライブラリを求める.この例ではWindowsである.
$LibraryPathの設定方法についてのさらなる情報は「ライブラリを探す」を参照されたい.
関数名
LibraryFunctionLoadの第2引数はロードする関数の名前を与える.この関数は「Libraryの構成」に記載のように,ライブラリからエキスポートされなければならない.ライブラリをC++としてコンパイルする場合は,おそらくCの命名規則でエキスポートしなければならないだろう.これについても「Libraryの構成」に記載されている.
型指定
LibraryFunctionLoadの第3,第4引数は,引数と戻り値の型を指定するものである.
"Boolean" | mbool | ブール値 |
Integer | mint | 機械整数 |
Real | double | 機械倍精度数 |
Complex | mcomplex | 機械倍精度複素数 |
{base,rank} | MTensor | 指定の基底型と階数のテンソル |
{base,rank,memory} | MTensor | 指定のメモリ管理のあるテンソル |
LibraryDataType[SparseArray,…] | MSparseArray | 疎配列 |
LibraryDataType[NumericArray,…] | MNumericArray | 数値配列 |
LibraryDataType[ByteArray] | MNumericArray | バイト配列 |
LibraryDataType[Image,…] | MImage | 2D画像 |
LibraryDataType[Image3D,…] | MImage | 3D画像 |
"UTF8String" | char * | UTF8文字列 |
LinkObject | WSLINK | WSTPに書き出された引数と結果 |
"Void" | 結果なし(戻るのみ) |
テンソルを指定する型はWolfram言語のパックアレーに直接マップしするように,また,テンソルをWolfram言語コンパイラで使用するように設計されている.これによりシステムの効率が格段によくなり,ライブラリがテンソル操作を利用できるようになる.テンソルは各要素の方と型と階数を明示的に指定することも,指定せずにおくこともできる.型と階数を指定しないでおくと,テンソルに作用するアプリケーションの柔軟性が増す.テンソルはInteger,Real,Complexについてのみサポートされていることに注意されたい.
テンソルが引数として渡される場合,そのメモリがどのように扱われるかも指定できる.これについては次のセクションで詳細を述べる.
ライブラリ関数のコードの中で,引数の配列から各型のデータを集めることができる.これを簡単にする目的で,MArgument用のマクロが提供されている.そのサンプルを以下に示す.
mint I0 = MArgument_getInteger(Args[0]);
double R0 = MArgument_getReal(Args[1]);
mcomplex C0 = MArgument_getComplex(Args[2]);
MTensor T0 = MArgument_getMTensor(Args[3]);
ライブラリ関数から戻るときは,結果に割り当てなければならない.以下に倍精度数を保管する例を示す.
MArgument_setReal(Res, R1);
テンソル入力を任意の型と階数で指定した場合,実際の型と階数は以下のようにコールバック関数で知ることができる.
type = libData->MTensor_getType( T0);
rank = libData->MTensor_getRank( T0);
MTensor_getTypeは結果の型を表す整数値を返す.
ライブラリ関数が引数を取らない場合は,入力指定に空のリストを渡せばよい.
ライブラリ関数が結果を返さない場合は,戻り値指定"Void"が使える.
MTensorのメモリ管理
mint,double,mcomplex等の型は,C関数の呼出しで一般的なように,ライブラリへ,またはライブラリから値で渡される.ライブラリにおける使い方はWolfram言語での使い方と全く異なる.
反対に,MTensorはデータ構造へのポインタであり,参照として渡される.そのため,メモリがどのように管理されるのかを考えなければならない.Wolfram言語は安全で簡単なデフォルトの方法を選択するが,大量のデータを渡したいときや,あとで使うためにデータを保存しておきたい場合は,その管理を考える必要がある.
MTensorの入力引数
MTensorをライブラリ関数に渡すときは,どのようにして渡すかを指定する数々のオプションが使える.
{Integer, 1} | MTensorのコピーを渡し,自動的に解放する |
{Integer, 1, Automatic} | MTensorのコピーを渡し,自動的に解放する |
{Integer, 1, "Constant"} | MTensorへの変更できない参照を渡す |
{Integer, 1, "Manual"} | MTensorのコピーを渡すが,自動的には解放しない |
{Integer, 1, "Shared"} | ライブラリとWolfram言語の間で共有されているMTensorへの参照を渡す |
自動渡し
自動渡しを選択した場合は,MTensorは関数が呼ばれる前にコピーされ,関数が戻るときに解放される.これは以下のような関数に適していると考えられる.
DLLEXPORT int demo_TI_R( WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint I0;
mreal R0;
int err = LIBRARY_NO_ERROR;
T0 = MArgument_getMTensor(Args[0]);
I0 = MArgument_getInteger(Args[1]);
err = libData->MTensor_getReal(T0, &I0, &R0);
if (err) return err;
MArgument_setReal(Res, R0);
return LIBRARY_NO_ERROR;
}
この関数では,MTensorを解放する必要はない.しかし,MTensorへの参照を保存して関数の終了後に使うことはできない.
自動渡しで渡されたMTensorは,関数呼出しがアクティブである限り,ライブラリによってのみ読み書きアクセスの両方ができるように所有される.
不変渡し
不変渡しを選択した場合は,MTensorへの参照が渡され,関数はMTensorを変更することはないと想定される.これにより,実質的にはMTensorデータへの高速リードオンリーアクセスが可能となる.コードがこの想定に反してデータを変更すると,そのWolfram言語セッションで重大なエラーが生じる可能性がある.
不変渡しで渡されたMTensorは,関数呼出しがアクティブである限り,ライブラリによってのみ読み書きアクセスの両方ができるように所有される.
手動渡し
手動渡しを選択した場合は,MTensorは関数呼出しの前にコピーされ,関数が戻るときに解放されない.これは以下のような関数に適していると考えられる.
DLLEXPORT int demo1_TI_R(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint I0;
mreal R0;
int err = LIBRARY_NO_ERROR;
T0 = MArgument_getMTensor(Args[0]);
I0 = MArgument_getInteger(Args[1]);
err = libData->MTensor_getReal( T0, &I0, &R0);
libData->MTensor_free(T0);
if (err) return err;
MArgument_setReal(Res, R0);
return LIBRARY_NO_ERROR;
}
関数がどのようにMTensorを解放したかに注目されたい.解放されなかったら,メモリは失われる.しかしテンソルを解放する代りに,テンソルを保存してそれをライブラリの他の部分で利用することもできる.最後にテンソルを使い終わったら,MTensor_freeを呼び出さなければならない.代りに,テンソルをライブラリ関数からWolfram言語に返し,所有者をWolfram言語に戻すという方法でもよい.
手動渡しで渡されたMTensorは完全にライブラリが所有し,この状態はMTensorが解放されるかWolfram言語に戻されるまで続く.
共有渡し
共有渡しを選択した場合は,MTensorは関数が呼び出される前にはコピーされず,ライブラリ関数に直接渡される.これは以下のような関数に適していると考えられる.
DLLEXPORT int demo2_TI_R(WolframLibraryData libData, mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint I0;
mreal R0;
int err = LIBRARY_NO_ERROR;
T0 = MArgument_getMTensor(Args[0]);
I0 = MArgument_getInteger(Args[1]);
err = libData->MTensor_getReal( T0, &I0, &R0);
libData->MTensor_disown( T0);
if (err) return err;
MArgument_setReal(Res, R0);
return LIBRARY_NO_ERROR;
}
引数に共有渡しを使うと,MTensorはWolfram言語とライブラリの間で共有される.Wolfram言語はMTensorを表の中に保管し,MTensorのメモリが収集されないようにする.ライブラリがMTensorを使う必要がなくなったら,MTensor_disownを呼び出さなければならない.
共有渡しで渡されたMTensorはライブラリとWolfram言語の間で共有される.この状態はライブラリとWolfram言語が完全に使い終えるまで続く.
引数の渡し方に"Shared"を使ってサンプル関数をロードする.
パックアレーではない引数で関数を呼び出すと,呼出しは動作するが,警告メッセージが返される.これはWolfram言語が入力をパックアレーに変換しなければならないため,データをコピーし,共有渡しの利点の一つを失うからである.
MTensorの返し方
ライブラリ関数からMTensorを返すときは,メモリをどのように扱うかも制御する.
{Integer, 1} | MTensorへの参照をWolfram言語に返す |
{Integer, 1, Automatic} | MTensorへの参照をWolfram言語に返す |
{Integer, 1, "Shared"} | ライブラリとWolfram言語の間で共有されているMTensorへの参照を返す |
ライブラリ関数からのMTensorの結果に対する可能なメモリ管理
自動返し
自動返しを選択すると,MTensorはライブラリからWolfram言語に直接戻る.Wolfram言語はそれをライブラリ関数の結果として使う.
DLLEXPORT int demo_I_T(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res) {
MTensor T0;
mint i, I0, dims[1];
int err = LIBRARY_NO_ERROR;
I0 = MArgument_getInteger(Args[0]);
dims[0] = I0;
err = libData->MTensor_new(MType_Integer, 1, dims, &T0);
for ( i = 1; i <= I0 && !err; i++) {
err = libData->MTensor_setInteger( T0, &i, i*2);
}
MArgument_setMTensor(Res, T0);
return err;
}
ライブラリがMTensorを所有している場合,つまりMTensorがライブラリ内で作られたり手動渡しで渡されたりした場合は,一旦MTensorがWolfram言語 に返されるとそれはもはやライブラリの所有ではなくなるため,ライブラリはMTensorを一切使わなくなる.これは上記関数に示されている.
MTensorがライブラリとWolfram言語の間で共有されているなら,自動返しはMTensorの所有権については変更を加えない.しかし,Wolfram言語はすでにMTensorへの参照を持っているため,ライブラリから共有MTensorを返すというのは奇妙である.
共有返し
共有返しを選択すると,MTensorはライブラリとWolfram言語の間で共有される.技術的な観点から言うと,これは関数が戻るときに共有表に加えることで実現される.これにより,MTensorが収集されないようにする.ライブラリがMTensorを必要としなくなったら,MTensor_disown(またはMTensor_disownAll)を呼び出さなければならない.
MTensorを共有表に加える前にMTensor_disown(またはMTensor_disownAll)を呼び出すと効果がなくなり,警告メッセージが示される.
MTensorのメモリ管理のまとめ
MTensorのためのメモリ管理の3つの方法を理解する方法の一つは,MTensorがさまざまな要素の間でどのように所有されるかを考えるというものである.
自動渡しでは,ライブラリへの呼出しがアクティブなときは,MTensorはそのライブラリ関数のみが所有する.関数はMTensorが変更されていてもいなくてもMTensorを返すことができるが,関数が戻った後は何があってもMTensorを使うことはできない.
手動渡しでは,関数が呼ばれた後,MTensorはライブラリが所有する.MTensorは,例えば別の関数の中のその関数の後はいつでも使える.MTensorはライブラリ関数が返すかMTensor_freeへの呼出しに渡されるまで,ライブラリが所有し続ける.手動渡しで渡されたMTensorを戻したいが,所有主はライブラリにしておきたい場合は,MTensor_cloneを呼び出してコピーするか,共有返しを使う関数から戻すかすればよい.
共有渡しと共有返しでは,MTensorの所有権はWolfram言語とライブラリの間で共有される.MTensor用にパックアレーを持つWolfram言語式が使えなくなったら,所有権はWolfram言語にはなくなる.ライブラリもMTensorに対してMTensor_disownが呼び出されるまでは所有権を持ち続ける.MTensorを関数に渡し,それが関数から返されるのと同じ回数だけMTensorにMTensor_disownを呼び出さなければいけないことに注意.例えば同じパックアレーをライブラリに3回渡したら,MTensor_disownを3回呼び出さなければならない.関数MTensor_disownAllはすべての参照を取り除くのに便利であり,MTensor_shareCountはMTensorが共有された実際の回数を与える.
最後にもう一つ覚えておかなければならないのは,何らかのエラーが生じた場合,これらがメモリを解放するライブラリの一部から制御を移し去ることがあるということである.この場合は,自分でエラーハンドラを挿入した方がよいかもしれない.これについてはエラーについてのセクションに記載されている.
文字列引数
文字列は文字コードUTF8で渡される.文字列が引数として渡されると,文字列のためのメモリ管理は完全にプログラムに任される.これはMTensorの手動渡しに似ている.
DLLEXPORT int countSubstring(WolframLibraryData libData,
mint Argc, MArgument *Args, MArgument Res)
{
char *instring = MArgument_getUTF8String(Args[0]);
char *substring = MArgument_getUTF8String(Args[1]);
mint i, n = strlen(instring);
mint slen = strlen(substring);
mint c = 0;
if (n > slen) {
n -= slen;
for (i = 0; i <= n; i++) {
if (!strncmp(instring + i, substring, slen)) {
c++;
}
}
}
MArgument_setInteger(Res, c);
libData->UTF8String_disown(instring);
libData->UTF8String_disown(substring);
return LIBRARY_NO_ERROR;
}
両方の文字列のメモリがUTF8String_disownを使ってどのように解放されるかに注目されたい.これを行わないか,プログラムの中で文字列引数への参照を維持しない場合は,メモリは単純に失われる.文字列が結果として返される場合は,Wolfram言語はメモリにアクセスしてそれをWolfram言語の内部文字列形式に返還するが,メモリの解放は試みない.そのため,プログラムが結果に文字列を割り当てると,そのメモリを解放する必要が出てくるが,結果の文字列を設定するのとは別の関数でなければならない.これは,ライブラリ関数が返された後にWolfram言語が文字列メモリにアクセスする必要があるからである.
MSparseArray
Wolfram言語のSparseArrayはLibraryFunctionに渡すことも,LibraryFunctionから渡されることもできる.そこではSparseArrayは型MSparseArrayの引数または結果として現れる.MSparseArrayはデータ構造のポインタであり,MTensorの場合と同様に,参照として渡される.そのため,メモリがどのように管理されるのかを考えなければならない.Wolfram言語は安全で簡単なデフォルトの方法を選ぶが,多量のデータを渡したい場合や今後使えるように保存したい場合は,その管理方法を考える必要がある.ほとんどの場合,MSparseArrayのメモリ管理はMTensorのメモリ管理と同様である.
SparseArrayの型指定
型MSparseArrayの引数および結果はLibraryDataTypeを使って指定する.
LibraryDataType[SparseArray] | 任意の型,あるいは任意の非零の階数の疎配列 |
LibraryDataType[SparseArray,type] | 指定の type および任意の非零階数の疎配列 |
LibraryDataType[SparseArray,type,rank] | 指定の type および rank の疎配列 |
MSparseArray引数または結果に対するLibraryFunctionの型指定
型MSparseArrayの引数は,LibraryFunctionに渡された実際の引数を,まだ存在しないならばWolfram言語SparseArrayオブジェクトに変換することにより管理される.変換が成功し,階数が引数指定により許可されたら,型強制は終了する.型が指定されていない場合は,明示的な値と暗示的な値が同じ機械数の型になるように強制が行われる.型が指定されている場合は,指定の型の明示的および暗示的値両方の強制が試みられる.
型MSparseArrayの結果は,階数もしくは次元のうちの一つが0でない限りSparseArrayオブジェクトとしてWolfram言語に返される.階数が0の場合結果は数に,次元の一つが0の場合は空のリストに変換される.
MSparseArrayのデータ構造
MSparseArrayはWolfram言語におけるSparseArrayの内部構造を反映している.Wolfram言語内部の構造は通常知っている必要はないが,関数を使用するに当たって知っていると役に立つこともある.
MSparseArrayは疎行列の圧縮行格納形式の拡張として保存される.明示的に保存された 個の位置を持つ × 行列では,それぞれの位置に対応する値は,長さ で階数1のMTensorに保存される.次元{nz,1}で整数型の階数2のMTensorは行1から までの列指標を保存するのに使われる.長さ で整数型の階数1のMTensorは,各行,つまり行ポインタにおける位置の累積数を保存するために使われる.このMTensor最後の要素は常に に等しい.
例えば,非零の値がすべて明示的に保存されている行列を考える.
非零の の値は{1., 2., 1., 4., 3., 1.}である.
列MTensorは{{1}, {1}, {2}, {1}, {3}, {4}}を含んでいる.
行ポインタMTensorは{0, 1, 3, 5, 6}を含んでいる.
この形式は以下のように任意の階数の配列に拡張される.係数が1ならば,長さ のベクトルは実質的に1× 行列として保存される.回数 が2より大きければ,列は次元{nz,r-1}のMTensorとして保存される.
Wolfram言語ではSparseArrayオブジェクトのInputFormの最後の2つの部分を見ることにより保存形式を知ることができる.
ほとんどの場合,暗示的な値は0であり,SparseArrayではこれがデフォルトである.しかし,以下のようにどのような値でも使うことができる.
上の例では暗示的な値は1であるが,データ構造は実質的に同じである.SparseArrayでは,暗示的な値は常に階数0のMTensorとして保存される.このMTensorの型は,MSparseArrayのデータの型である.値は(あるとすれば),暗示的な値と一致した型を持つ.
データ構造で使われるMTensorsは,ライブラリコールバック関数を使ってアクセスすることができる.
MSparseArray_getImplicitValue | 暗示的な値を含むMTensorのポインタを取得する. |
MSparseArray_getExplicitValues | 明示的な値を含むMTensorのポインタを取得する. |
MSparseArray_getColumnIndices | 列指標を含むMTensorのポインタを取得する. |
MSparseArray_getRowPointers | 行ポインタ配列を含むMTensorのポインタを取得する. |
MSparseArrayのCSRデータにアクセスするためのコールバック関数.
MSparseArrayコールバック関数はすべてWolframLibraryDataのsparseLibraryFunctionsフィールドにあり,WolframSparseLibrary.hで宣言されている.従って,通常以下のように使われる.
#include "WolframLibrary.h"
#Include "WolframSparseLibrary.h"
....
MTensor *t;
MSparseArray s;
...
t = (*libData->sparseLibraryFunctions->MSparseArray_getImplicitValue)(s);
...
これらのコールバック関数はすべてMTensorへのポインタを返す.MTensorはMSparseArrayデータ構造に属しており,MTensor_freeを使って解放されてはならない.コールバック関数はMSparseArrayが解放されるときに解放される.これらのMTensors内部のデータにアクセスすることにより,データ構造の値をその場で変更することができる.MSparseArrayのメモリがライブラリによって所有されている限り,明示的な値は簡単に変更することができる.暗示的なの変更も行われるが,暗示的な値の変更は上記の例のように異なる疎構造を意味することがある.そのため新しい値に対するCSRデータ構造を再計算するコールバック関数MSparseArray_resetImplicitValueがある.列指標や行ポインタの変更は,細心の注意を払って行う必要がある.これらは一貫性がなければならないので,誤った変更を行うと,大きな問題につながることがある.
CSR格納を直接構築したり変更したりすることは難しい場合がある.通常表現される配列の実際の位置で作業する方が簡単である.
MSparseArray_fromExplicitPositions | 明示的な位置および値からMSparseArrayを構築する |
MSparseArray_getExplicitPositions | 明示的な位置を含む階数2のMTensorを返す |
MSparseArray_getExplicitPositionsによって構築されたMTensorは,ライブラリに属するので,使い終わったら解放される.
Wolfram言語では,SparseArrayオブジェクトを構築するためのSparseArray[{pos1->val1,pos2->val2,…}]シンタックスはコールバックMSparseArray_fromExplicitPositionsに相当し,関数ArrayRulesはMSparseArray_getExplicitValuesと組み合されたコールバックMSparseArray_getExplicitPositionsに相当する.
MSparseArrayの入力引数のメモリ管理
MSparseArrayをライブラリ関数に渡すとき,どのように行うかを決定するオプションがいくつかある.
LibraryDataType[SparseArray,…] | MSparseArrayのコピーを渡し,自動的に解放する |
{LibraryDataType[SparseArray,…],Automatic} | MSparseArrayのコピーを渡し,自動的に解放する |
{LibraryDataType[SparseArray,…],"Constant"} | MSparseArrayへの変更できない参照を渡す |
{LibraryDataType[SparseArray,…],"Manual"} | MSparseArrayのコピーを渡すが,自動的には解放しない |
{LibraryDataType[SparseArray,…],"Shared"} | ライブラリとWolfram言語の間で共有されているMSparseArrayへの参照を渡す |
ライブラリ関数へのMSparseArray引数の可能なメモリ管理
自動渡し
自動渡しを選択した場合は,MSparseArrayは関数が呼ばれる前にコピーされ,関数が戻るときに解放される.
自動渡しで渡されたMSparseArrayは,関数呼出しがアクティブである限り,ライブラリ関数によってのみ読み書きアクセスの両方ができるように所有される.
不変渡し
不変渡しを選択した場合は,MSparseArrayへの参照が渡され,関数はMSparseArrayを変更することはないと想定される.これにより,実質的にはMSparseArrayデータへの高速リードオンリーアクセスが可能となる.コードがこの想定に反してデータを変更すると,そのWolfram言語セッションで重大なエラーが生じる可能性がある.
不変渡しで渡されたMSparseArrayは,関数呼出しがアクティブである限り,ライブラリ関数によって読取りのみのアクセスが可能である.
手動渡し
手動渡しを選択した場合は,MSparseArrayは関数呼出しの前にコピーされ,関数が戻るときに解放されない.
手動渡しで渡されたMSparseArrayは完全にライブラリが所有し,この状態はMSparseArrayが解放されるかWolfram言語に戻されるかするまで続く.
共有渡し
共有渡しを選択した場合は,MSparseArrayは関数が呼び出される前にはコピーされず,ライブラリ関数に直接渡される.
引数に共有渡しを使うと,MSparseArrayはWolfram言語とライブラリの間で共有される.Wolfram言語はMSparseArrayを表の中に保管し,MSparseArrayのメモリが収集されないようにする.ライブラリがMSparseArrayを使う必要がなくなったら,MSparseArray_disownを呼び出さなければならない.
共有渡しで渡されたMSparseArrayはライブラリとWolfram言語の間で共有される.この状態はライブラリとWolfram言語が完全に使い終えるまで続く.
MTensorの場合と同様に,与えられたWolfram言語引数がSparseArrayであり,MSparseArrayを作成するために明示的あるいは暗示的値の強制が必要ない場合のみデータが共有される.
MSparseArrayの返し方
ライブラリ関数からMSparseArrayを返すときは,メモリをどのように扱うかも制御する.
LibraryDataType[SparseArray,…] | MSparseArrayへの参照をWolfram言語のSparseArrayとして返す |
{LibraryDataType[SparseArray,…],Automatic} | MSparseArrayへの参照をWolfram言語のSparseArrayとして返す |
{LibraryDataType[SparseArray,…],"Shared"} | ライブラリとWolfram言語の間で共有されているMSparseArrayへの参照をWolfram言語のSparseArrayとして返す |
ライブラリ関数からのMSparseArrayの結果に対する可能なメモリ管理
自動返し
自動返しを選択すると,MSparseArrayはライブラリからWolfram言語に直接戻る.Wolfram言語はそれをライブラリ関数の結果として使う.
ライブラリがMSparseArrayを所有している場合,つまりMSparseArrayがライブラリ内で作られたり手動渡しで渡されたりした場合は,一旦MSparseArrayがWolfram言語に返されるとそれはもはやライブラリの所有ではなくなるため,ライブラリはMSparseArrayを一切使わなくなる.
MSparseArrayがライブラリとWolfram言語の間で共有されているなら,自動返しはMSparseArrayの所有権については変更を加えない.しかし,Wolfram言語はすでにMSparseArrayへの参照を持っているため,ライブラリから共有MSparseArrayを返すというのは奇妙である.
共有返し
共有返しを選択すると,MSparseArrayはライブラリとWolfram言語の間で共有される.技術的な観点から言うと,これは関数が戻るときに共有表に加えることで実現される.これにより,MSparseArrayが収集されないようにする.ライブラリがMSparseArrayを必要としなくなったら,MSparseArray_disown(またはMSparseArray_disownAll)を呼び出さなければならない.
MSparseArrayを共有表に加える前にMSparseArray_disown(またはMSparseArray_disownAll)を呼び出すと効果がなくなり,警告メッセージが示される.
MNumericArray [実験的]
Wolfram言語のNumericArrayオブジェクトとByteArrayオブジェクトはLibraryFunctionに渡すことも,LibraryFunctionから渡されることもできる.そこではれらは型MNumericArrayの引数または結果として現れる.MNumericArrayはデータ構造へのポインタであり,MTensorの場合と同様に,参照として渡される.そのため,メモリがどのように管理されるのかを考えなければならない.Wolfram言語は安全で簡単なデフォルトの方法を選ぶが,大量のデータを渡したいときや,あとで使うためにデータを保存しておきたい場合は,その管理を考える必要がある.多くの場合は,MNumericArrayのメモリ管理はMTensorのメモリ管理方法と非常に似ている.
型指定
型MNumericArrayの引数または結果はLibraryDataTypeを使って指定する.
LibraryDataType[NumericArray] | 任意の型または非零の階数の数値配列 |
LibraryDataType[NumericArray,type] | 指定の type および任意の非零の階数の数値配列 |
LibraryDataType[NumericArray,type,rank] | 指定の type と rank の数値配列 |
LibraryDataType[ByteArray] | バイト配列 |
MNumericArrayの引数または結果のためのLibraryFunctionの型指定
式の型MNumericArrayの引数 arg は, arg がWolfram言語のNumericArrayオブジェクトかByteArrayオブジェクトであるかどうか確認することで管理される.その後,式に含まれるMNumericArrayのデータ構造への参照が関数に渡される.
型MNumericArrayの結果は必ずNumericArrayオブジェクトまたはByteArrayオブジェクトとしてWolfram言語に返される.
MNumericArrayデータ構造
MNumericArrayはWolfram言語のNumericArrayオブジェクトとByteArrayオブジェクトの内部構造を反映している.MNumericArrayデータ構造は配列についての情報を含み,その多くがコールバック関数でアクセスできる.
MNumericArrayデータ構造の最も重要な部分は,NumericArray型に対応する型のデータの配列である.ByteArrayは常に"UnsignedInteger8"型である.
型
|
Cの型
|
説明
|
"Integer8" | MNumericArray_Type_Bit8 | から |
"UnsignedInteger8" | MNumericArray_Type_UBit8 | 0から255 |
"Integer16" | MNumericArray_Type_Bit16 | から |
"UnsignedInteger16" | MNumericArray_Type_UBit16 | 0から65535 |
"Integer32" | MNumericArray_Type_Bit32 | から |
"UnsignedInteger32" | MNumericArray_Type_UBit32 | 0から232-1 |
"Integer64" | MNumericArray_Type_Bit64 | から |
"UnsignedInteger64" | MNumericArray_Type_UBit64 | 0から264-1 |
"Real32" | MNumericArray_Type_Real32 | 単精度実数(32ビット) |
"Real64" | MNumericArray_Type_Real64 | 倍精度実数(64ビット) |
"ComplexReal32" | MNumericArray_Type_Complex_Real32 | 単精度複素数 |
"ComplexReal64" | MNumericArray_Type_Complex_Real64 | 倍精度複素数 |
NumericArray型と対応するCの型
配列データにアクセスするためのコールバック関数が用意されている.
配列型を取得し,サポートされている全てのデータ型間で変換するためには,MNumericArray_getType関数とMNumericArray_convertType関数がある.また,MNumericArrayの階数と次元についての情報を返す関数MNumericArray_getRank,MNumericArray_getDimensions,MNumericArray_getFlattenedLengthもある.
MNumericArrayコールバック関数はすべてWolframLibraryDataのnumericarrayLibraryFunctionsフィールドにあり,WolframNumericArrayLibrary.hで宣言されているので,一般には以下のように使用する.
#include "WolframLibrary.h"
#Include "WolframNumericArrayLibrary.h"
....
MNumericArray na_in = NULL, na_out = NULL;
numericarray_data_t type = MNumericArray_Type_Undef;
WolframNumericArrayLibrary_Functions naFuns = libData->numericarrayLibraryFunctions;
...
na_in = MArgument_getMNumericArray(Args[0]);
type = naFuns->MNumericArray_getType(na_in);
if(type != MNumericArray_Type_Real32) {
na_out = naFuns->MNumericArray_convertType(na_in, MNumericArray_Type_Real32,
MNumericArray_Convert_Clip_Round);
}
...
MNumericArrayの入力引数のメモリ管理
MNumericArrayをライブラリ関数に渡すときのメモリの管理方法を決定するさまざまなオプションがある.
LibraryDataType[NumericArray,…] | MNumericArrayのコピーを渡し,自動的に解放する |
{LibraryDataType[NumericArray,…],Automatic} | MNumericArrayのコピーを渡し,自動的に解放する |
{LibraryDataType[NumericArray,…],"Constant"} | MNumericArrayへの変更できない参照を渡す |
{LibraryDataType[NumericArray,…],"Manual"} | MNumericArrayのコピーを渡すが,自動的には解放しない |
{LibraryDataType[NumericArray,…],"Shared"} | ライブラリとWolfram言語の間で共有されているMNumericArrayへの参照を渡す |
ライブラリ関数へのNumericArray引数の可能なメモリ管理
同じオプションがByteArrayオブジェクトにも使用できる.
自動渡し
自動引き渡しを選択した場合は,MNumericArrayは関数が呼ばれる前にコピーされ,関数が戻るときに解放される.
自動渡しで渡されたMNumericArrayは,関数呼出しがアクティブである限り,ライブラリ関数によってのみ読み書きアクセスの両方ができるように所有される.
不変渡し
不変渡しを選択した場合は,MNumericArrayへの参照が渡され,関数はMNumericArrayを変更することはないと想定される.これにより,実質的にはMNumericArrayデータへの高速リードオンリーアクセスが可能となる.コードがこの想定に反してデータを変更すると,そのWolfram言語セッションで重大なエラーが生じる可能性がある.
不変渡しで渡されたMNumericArrayは,関数呼出しがアクティブである限り,ライブラリによって読取りのみのアクセスが可能である.
手動渡し
手動渡しを選択した場合は,MNumericArrayは関数呼出しの前にコピーされ,関数が戻るときに解放されない.
手動渡しで渡されたMNumericArrayは完全にライブラリが所有し,この状態はMNumericArrayが解放されるかWolfram言語に戻されるまで続く.
共有渡し
共有渡しを選択した場合は,MNumericArrayは関数が呼び出される前にはコピーされず,ライブラリ関数に直接渡される.
引数に共有渡しを使うと,MNumericArrayはWolfram言語とライブラリの間で共有される.Wolfram言語はMNumericArrayを表の中に保管し,MNumericArrayのメモリが収集されないようにする.ライブラリがMNumericArrayを使う必要がなくなったら,MNumericArray_disownを呼び出さなければならない.
共有渡しで渡されたMNumericArrayはライブラリとWolfram言語の間で共有される.この状態はライブラリとWolfram言語が完全に使い終えるまで続く.
MNumericArrayの返し方
ライブラリ関数からMNumericArrayを返すときは,メモリをどのように扱うかも制御する.
LibraryDataType[NumericArray,…] | MNumericArrayへの参照をWolfram言語のNumericArrayとして返す |
{LibraryDataType[NumericArray,…],Automatic} | MNumericArrayへの参照をWolfram言語のNumericArrayとして返す |
{LibraryDataType[NumericArray,…],"Shared"} | ライブラリとWolfram言語の間で共有されているMNumericArrayへの参照をWolfram言語のNumericArrayとして返す |
LibraryDataType[ByteArray] | MNumericArrayへの参照をWolfram言語のByteArrayとして返す |
{LibraryDataType[ByteArray],Automatic} | MNumericArrayへの参照をWolfram言語のByteArrayとして返す |
{LibraryDataType[ByteArray],"Shared"} | ライブラリとWolfram言語の間で共有されているMNumericArrayへの参照をWolfram言語のByteArrayとして返す |
ライブラリ関数からのMNumericArrayの結果に対する可能なメモリ管理
自動返し
自動返しを選択すると,MNumericArrayはライブラリからWolfram言語に直接戻る.Wolfram言語はそれをライブラリ関数の結果として使う.
ライブラリがMNumericArrayを所有している場合,つまりMNumericArrayがライブラリ内で作られたり手動渡しで渡されたりした場合は,一旦MNumericArrayがWolfram言語 に返されるとそれはもはやライブラリの所有ではなくなるため,ライブラリはMNumericArrayを一切使わなくなる.
MNumericArrayがライブラリとWolfram言語の間で共有されているなら,自動返しはMNumericArrayの所有権については変更を加えない.しかし,Wolfram言語はすでにMNumericArrayへの参照を持っているため,ライブラリから共有MNumericArrayを返すというのは奇妙である.
共有返し
共有返しを選択すると,MNumericArrayはライブラリとWolfram言語の間で共有される.技術的な観点から言うと,これは関数が戻るときに共有表に加えることで実現される.これにより,MNumericArrayが収集されないようにする.ライブラリがMNumericArrayを必要としなくなったら,MNumericArray_disown(またはMNumericArray_disownAll)を呼び出さなければならない.
MNumericArrayを共有表に加える前にMNumericArray_disown(またはMNumericArray_disownAll)を呼び出すと効果がなくなり,警告メッセージが示される.
MImage
Wolfram言語のImageおよびImage3DはLibraryFunctionに渡すことも,LibraryFunctionから渡されることもできる.そこではこれらは型MImageの引数,または結果として現れる.MImageはデータ構造のポインタであり,MTensorの場合と同様に,参照として渡される.そのため,メモリがどのように管理されるのかを考えなければならない.Wolfram言語は安全で簡単なデフォルトの方法を選ぶが,多量のデータを渡したい場合や今後使えるように保存したい場合は,その管理方法を考える必要がある.ほとんどの場合,MImageのメモリ管理はMTensorのメモリ管理と同様である.
MImageの型指定
型MImageの引数および結果は,LibraryDataTypeを使って指定する.
LibraryDataType[Image] | 任意の型の2D画像 |
LibraryDataType[Image3D] | 任意の型の3D画像 |
LibraryDataType[ImageImage3D] | 任意の型の2Dまたは3D画像 |
LibraryDataType[imd,type] | imd で指定された次元を持つ指定された型の画像 |
MImageの引数または結果に対するLibraryFunctionの型指定.
式の型MImageの引数 arg は,arg が引数指定によって許可された次元数と矛盾のないWolfram言語のImageまたはImage3Dオブジェクトであるかどうかをチェックすることにより管理される.画像型が指定され,実際の型が指定された方と一致しない場合は,画像型の強制にImage[arg,type]と同等のものが使われる.それから,画像式に含まれるMImageデータ構造への参照が関数に渡される.
型MImageの結果は常にImageまたはImage3DオブジェクトとしてWolfram言語に返される.
MImageのデータ構造
MImageはWolfram言語のImageおよびImage3Dの内部構造を反映している.MImageデータ構造は画像についての情報を含んでおり,そのほとんどはコールバック関数からアクセスできる.
MImageデータ構造の最も重要な部分は,画像の型に対応するデータ配列の型である.
型
|
C型
|
説明
|
"Bit" | raw_t_bit | 整数0か1(単一ビット) |
"Byte" | raw_t_ubit8 | 整数0から255まで(8ビット) |
"Bit16" | raw_t_ubit16 | 整数0から65535まで(16ビット) |
"Real32" | raw_t_real32 | 単一精度実数(32ビット) |
"Real" | raw_t_real64 | 倍精度実数(64ビット) |
この配列の並び方は次元数,チャンネル,インターリーブにより異なる.配列の単一画素への配列へのアクセスを与えるコールバック関数が提供されており,チャンネルがどのようにインターリーブするかを知っている必要はない.
MImage_getBit | 指定された画素およびチャンネルにおけるビット値を取得する |
MImage_setBit | 指定された画素およびチャンネルにおけるビット値を指定する |
5つの画像型のそれぞれに対してMImage_gettype およびMImage_settype関数がある.
MImageコールバック関数は, すべてWolframLibraryDataのimageLibraryFunctionsフィールドにあり,WolframImageLibrary.hで宣言されている.従って,通常以下のように使われる.
#include "WolframLibrary.h"
#Include "WolframImageLibrary.h"
....
int err;
raw_t_bit v;
mint channel, pos[3];
MImage img;
...
err = (*libData->imageLibraryFunctions->MImage_getBit)(img, pos, channel, &v);
...
多くの操作で配列全体へのアクセスが必要とされるが,画素,チャンネル,配列指標を対応させるためにどのようにチャンネルがインターリーブされるかを知る必要がある.
MImage_interleavedQ | チャンネルデータがインターリーブされているかどうかを調べる |
MImage_getBitData | 画像型が"Bit"である画像に対するraw_t_bitデータの配列を取得する |
MImage_getRawData | 任意の画像型の配列に対するvoid * ポインタを取得する |
MImageの入力引数のメモリ管理
MImageをライブラリ関数に渡すときは,どのようにして渡すかを指定する数々のオプションが使える.
LibraryDataType[Image,…] | MImageのコピーを渡し,自動的に解放する |
{LibraryDataType[Image,…],Automatic} | MImageのコピーを渡し,自動的に解放する |
{LibraryDataType[Image,…],"Constant"} | MImageへの変更できない参照を渡す |
{LibraryDataType[Image,…],"Manual"} | MImageのコピーを渡すが,自動的には解放しない |
{LibraryDataType[Image,…],"Shared"} | ライブラリとWolfram言語の間で共有されているMImageへの参照を渡す |
ライブラリ関数へのImage引数およびImage3D引数の可能なメモリ管理
自動渡し
自動渡しを選択した場合は,MImageは関数が呼ばれる前にコピーされ,関数が戻るときに解放される.
自動渡しで渡されたMImageは,関数呼出しがアクティブである限り,ライブラリによってのみ読み書きアクセスの両方ができるように所有される.
不変渡し
不変渡しを選択した場合は,MImageへの参照が渡され,関数はMImageを変更することはないと想定される.これにより,実質的にはMImageデータへの高速リードオンリーアクセスが可能となる.コードがこの想定に反してデータを変更すると,そのWolfram言語セッションで重大なエラーが生じる可能性がある.
不変渡しで渡されたMImageは,関数呼出しがアクティブである限り,ライブラリによって読取りのみのアクセスが可能である.
手動渡し
手動渡しを選択した場合は,MImageは関数呼出しの前にコピーされ,関数が戻るときに解放されない.
手動渡しで渡されたMImageは完全にライブラリが所有し,この状態はMImageが解放されるかWolfram言語に戻されるかするまで続く.
共有渡し
共有渡しを選択した場合は,MImageは関数が呼び出される前にはコピーされず,ライブラリ関数に直接渡される.
引数に共有渡しを使うと,MImageはWolfram言語とライブラリの間で共有される.Wolfram言語はMImageを表の中に保管し,MImageのメモリが収集されないようにする.ライブラリがMImageを使う必要がなくなったら,MImage_disownを呼び出さなければならない.
共有渡しで渡されたMImageはライブラリとWolfram言語の間で共有される.この状態はライブラリとWolfram言語が完全に使い終えるまで続く.
MTensorの場合と同様に,与えられたWolfram言語引数がImageであり,MImageを作成するために明示的あるいは暗示的値の強制が必要ない場合のみデータが共有される.
MImageの返し方
ライブラリ関数からMImageを返すときは,メモリをどのように扱うかも制御する.
LibraryDataType[Image,…] | MImageへの参照をWolfram言語のImageとして返す |
{LibraryDataType[Image,…],Automatic} | MImageへの参照をWolfram言語のImageとして返す |
{LibraryDataType[Image,…],"Shared"} | ライブラリとWolfram言語の間で共有されている参照を,Wolfram言語のImageとしてMImageに返す |
ライブラリ関数からのMImageの結果に対する可能なメモリ管理
自動返し
自動返しを選択すると,MImageはライブラリからWolfram言語に直接戻る.Wolfram言語はそれをライブラリ関数の結果として使う.
ライブラリがMImageを所有している場合,つまりMImageがライブラリ内で作られたり手動渡しで渡されたりした場合は,一旦MImageがWolfram言語に返されるとそれはもはやライブラリの所有ではなくなるため,ライブラリはMImageを一切使わなくなる.
MImageがライブラリとWolfram言語の間で共有されているなら,自動返しはMImageの所有権については変更を加えない.しかし,Wolfram言語はすでにMImageへの参照を持っているため,ライブラリから共有MImageを返すというのは奇妙である.
共有返し
共有返しを選択すると,MImageはライブラリとWolfram言語の間で共有される.技術的な観点から言うと,これは関数が戻るときに共有表に加えることで実現される.これにより,MImageが収集されないようにする.ライブラリがMImageを必要としなくなったら,MImage_disown(またはMImage_disownAll)を呼び出さなければならない.
MImageを共有表に加える前にMImage_disown(またはMImage_disownAll)を呼び出すと効果がなくなり,警告メッセージが示される.
管理されたライブラリ式
メモリがライブラリによって管理されているオブジェクトやデータのインスタンスに対するハンドラとして,Wolfram言語式を使いたいという場合もあろう.これらのインスタンスにアクセスするために,管理されたライブラリ式を使うことにより,式がWolfram言語セッションで参照されなくなったときにインスタンスを自動的に解放することが可能になる.
CreateManagedLibraryExpression | ライブラリデータに関連付けることのできる式を生成する |
ManagedLibraryExpressionQ | 式が,管理されたライブラリ式であるかどうかをテストする |
ManagedLibraryExpressionID | 管理されたライブラリ式に関連付けられた正の整数IDを与える |
registerLibraryExpressionManager(mname, mfun) | 名前が mname でマネージャ関数が mfun のライブラリ式マネージャを登録する |
unregisterLibraryExpressionManager(mname) | 名前が mname のライブラリ式マネージャの登録を解除する |
releaseManagedLibraryExpression(mname,mid) | マネージャが mname でIDが mid の,管理されたライブラリ式を解放する |
通常,ライブラリ式マネージャはライブラリの初期化で登録し,ライブラリの非初期化で登録解除するのが一番よい.それによりライブラリがロードされている限りマネージャはアクティブになる.
/* Initialize Library */
EXTERN_C DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData)
{
return (*libData->registerLibraryExpressionManager)("manager", manage_instance);
}
/* Uninitialize Library */
EXTERN_C DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData)
{
int err = (*libData->unregisterLibraryExpressionManager)("manager");
}
マネージャ関数 mfun の型はvoid (*mfun)(WolframLibraryData libData, mbool mode, mint id)である.mname がマネージャ関数 mfun で登録されているならば,CreateManagedLibraryExpression[mname, f]の評価によって,名前が mname のマネージャに対して一意である正の整数ID mid が生成される.まず f[mid]が評価され, mode 0で id が mid に等しい関数 mfun が呼ばれ,最後に評価 f[mid]が結果として戻る.Wolfram言語セッションのいずれかの時点で結果 f[mid]への参照が存在しなくなると,mode 1で id が mid に等しい関数 mfun が呼ばれる.通常,mode 0はデータの生成を,mode 1はデータの解放を意味する.
Wolfram言語式への隠れた参照を持つことが可能な場合もある.ライブラリデータがもはや必要なくなったとき,releaseManagedLibraryExpressionを使って式を解放することができる.式を解放するときに,releaseManagedLibraryExpression(mname, mid)は,mode 1で id が mid に等しい mname に関連付けられたマネージャ関数 fun を呼ぶ.
これを使って,簡単な線形合同法のさまざまなインスタンスを設定する例がdemo_managed.cxxに実装されており,demo_managedの中のLibraryLinkの例で示されている.
ライブラリコールバック関数
関数がそのタスクを実行するために,別の関数を繰返し評価する必要がある場合がある.その典型的な例が,根を求める関数と最適化の関数である.
非常に一般的な式に対するコールバックは常にWolfram Symbolic Transfer Protocol (WSTP)引数インターフェースを使って実行することができるが,通信のオーバーヘッドの危険性も備えている.比較的単純な関数を繰返し評価する場合は,オーバーヘッドを最小限に抑えることが重要である.このためには,引数の型に制約を付ける.
CompiledFunctionで許可される引数は,実際にはLibraryFunctionに許可される引数の部分集合であるため,コールバックのためにCompiledFunctionに接続することは, 妥当な一般性を保ちながらスピードを求めることにおいて,当然の選択である.さらにCompiledFunctionがCompilationTarget->"C"でコンパイルされていたら,コールバックは機械コードレベルで行うことができる.
ConnectLibraryCallbackFunction | CompiledFunctionに接続して,ライブラリから呼び出せるようにする |
registerLibraryCallbackManager(mname, mfun) | 名前が mname でマネージャ関数が mfun であるライブラリコールバックマネージャを登録する |
unregisterLibraryCallbackManager(mname) | 名前が mname であるライブラリコールバックマネージャの登録を解除する |
callLibraryCallbackFunction(id, libData, Args, Res) | IDが id である接続されたライブラリコールバック関数を呼び出す |
releaseLibraryCallbackFunction(id) | IDが id であるマネージャを含むライブラリコールバック関数を解放する |
ライブラリコールバック関数に対するC/C++のコールバック関数.
通常,ライブラリコールバックマネージャはライブラリの初期化で登録し,ライブラリの非初期化で登録解除するのが一番よい.それにより,ライブラリがロードされている限りマネージャはアクティブになる.
/* Initialize Library */
EXTERN_C DLLEXPORT int WolframLibrary_initialize( WolframLibraryData libData)
{
return (*libData->registerLibraryCallbackFunction)("manager", manage_callback);
}
/* Uninitialize Library */
EXTERN_C DLLEXPORT void WolframLibrary_uninitialize( WolframLibraryData libData)
{
int err = (*libData->unregisterLibraryCallbackFunction)("manager");
}
マネージャ関数 mfun の型はmbool (*mfun)(WolframLibraryData libData, mint id, MTensor argtypes)である.mname がマネージャ関数 mfun で登録されているならば,ConnectLibraryCallbackFunction[mname, cf]の評価によって,一意である正の整数ID mid が生成される.次に id が mid に等しく,argtypes が階数2のMTensor(各行が対応する引数引数と同じ型と階数を持つ)で関数 mfun が呼ばる.n 個の引数がある場合,argtypes の長さは n+1で最後の行は結果と同じ型および階数になる.型はWolframLibrary.hで定義されているように,マクロMType_Integer等を使って符号化される.階数0はスカラーに対応する.ConnectLibraryCallbackFunction[mname, cf]は mfun がTrueを返すときだけTrueを返す.CompiledFunction cf はreleaseLibraryCallbackFunction(mid)が呼ばれるまで保存され,CompiledFunctionへの参照が他になくなると,解放される.
一旦接続されると,関数は,与えられたIDを持つcallLibraryCallbackFunctionを使って呼び出すことができる.
コールバック機能の使い方を説明した例はdemo_callback.cに実装されており,demo_callbackの中のLibraryLinkの例で見ることができる.
LinkObjectの引数と結果
MTensorの標準数値型でカバーされていないライブラリ関数に引数を送りたい場合,またはそのライブラリから結果を取得したい場合は,引数と結果にLinkObjectが使える.これを使うと,どのようなWolfram言語式の構造でもライブラリに送ることができる.
ライブラリはLinkObjectの引数と結果については,基本数値型とは異なる方法で呼び出される.関数が呼ばれると,Wolfram言語は全引数のリストをWSTP接続に書き出す.これはWolfram言語がWSTP上で式を書き出すLinkWriteという方法で行われる.それからライブラリ関数が呼び出される.ライブラリ関数はリンクから引数を読み,作業を行い,リンク上に結果を書いて返す.それからWolfram言語はLinkReadというWolfram言語がWSTPから式を読み取る方法でリンクから結果を読む.
LinkObjectの引数と結果を使った例を以下に示す.
DLLEXPORT mint reverseString( WolframLibraryData libData, WSLINK mlp)
{
mint res = 0;
int i1, i2, sum;
int len;
const char *inStr = NULL;
char* outStr = NULL;
if ( !WSCheckFunction( mlp, "List", &len))
goto retPt;
if ( len != 1)
goto retPt;
if(! WSGetString(mlp, &inStr))
goto retPt;
if ( ! WSNewPacket(mlp) )
goto retPt;
outStr = reverseStringImpl(inStr);
res = WSPutString( mlp,outStr);
retPt:
if ( inStr != NULL)
WSReleaseString(mlp, inStr);
if ( outStr != NULL)
free(inStr);
return res;
}
LinkObjectの引数と結果を使ったサンプル関数をロードする.
与えられたライブラリ関数に割り当てられたLinkObjectは,それに続く呼出しで再利用される.そのため,ライブラリ関数が呼出のたびに完全にLinkObjectからの引数を読み込み,結果をLinkObjectへ書き込むことが重要である.これを行わないと,リンクが同期しなくなる可能性があり,その後,そのライブラリ関数が呼出せなくなることがある.
ライブラリ関数の呼出し中に放棄が生じたり,ライブラリ関数がエラーを返したりした場合,その関数のLinkObjectは即座に破棄され,関数が戻された後に再び割り当てられる.この場合,リンクはどの状態でおいておいても安全である.未解決の放棄はAbortQコールバックを使って検出できる.
Wolfram言語との通信にWSTPを使うには,"WolframLibrary.h"ヘッダの前に"mathlink.h"ヘッダがインクルードされていなければならない.
ライブラリを探す
LibraryFunctionLoadへの最初の引数はロードするライブラリである.これは/Library/myLibrary.dylibのような絶対ファイル名で与えることができる.しかし,パスから見た相対指定をライブラリに与える方がより便利である.また,ダイナミックライブラリの拡張子はプラットフォームによって異なるので,これは拡張子の指定においても問題となる.ダイナミックライブラリの拡張子の慣例を以下にまとめる.
ダイナミックライブラリに対応したWolfram言語の関数は自動的にこの問題を解決する.入力名に拡張子が付いていなければ,使用中のプラットフォームに適したものが加えられる.これによりどのマシンを使っているかに関係なくライブラリを操作することができる.またWolfram言語は,ライブラリの検索にパス$LibraryPathを提供する.
$LibraryPathの設定の例を以下に示す.ライブラリを含むWolfram言語アプリケーションが数多く含まれている.
FindLibraryはライブラリを探す関数である.これはLibraryFunctionLoad等の別のコマンドで呼び出される.FindLibraryはまずライブラリの拡張子を修正し,絶対名としてライブラリを捜し,最後に$LibraryPathでライブラリを探す.そして見付かったファイルを返す.
次の例では$LibraryPathを探し,使用中のプラットフォーム(この例ではWindows)に適したライブラリを見付ける.
拡張子付きの名前を使うこともできるが,それは拡張子が適しているプラットフォームでのみ動作することに注意されたい.
$LibraryPathには要素を加えることができる.しかし,多くのフォルダが自動的に加えられる.これには $UserBaseDirectory/Applicationsまたは$BaseDirectory/Applicationsに含まれるWolfram言語アプリケーションのLibraryResourcesフォルダも含まれる.これは J/Link がJavaクラスをロードすることができ,DatabaseLink がデータベースリソースがロードできる方法に似ている.これにより,Wolfram言語での作業をライブラリと組み合せる便利な方法が提供される.
独自のライブラリのインストール
Wolfram言語セッションで使うためにダイナミックライブラリをインストールしたい場合には,数々のオプションがある.以下にまとめる.
絶対パス名
LibraryFunctionLoad等の関数に絶対パス名を割り当てることができる.これは設定が簡単であるが,作業中のものを別のコンピュータに移動したりすると問題が生じる.
$LibraryPathの設定
ライブラリの場所を$LibraryPathに加えることができる.変数に場所を設定して何度も使えるため,これは絶対パス名を使う場合より抽象化されたものとなる.
さらに,Blockを使って$LibraryPathの設定を一時的に変更することもできる.以下に例を示す.
$BaseDirectoryと$UserBaseDirectory
$LibraryPathは常に$UserBaseDirectory/SystemFiles/LibraryResources/$SystemIDと$BaseDirectory/SystemFiles/LibraryResources/$SystemIDの2つの場所を含む.ここにライブラリを置けば,ライブラリをロードする関数によって見付けられる.
$BaseDirectoryの中の場所を示す.
アプリケーション
コードをWolfram言語アプリケーションとして作りたい場合は,$LibraryPathに置かれるライブラリを含むことができる.$SystemIDに合致するフォルダにディレクトリを含まなければならない.以下にアプリケーションの例を示す.
MyApplication
MyApplication.m
Kernel
init.m
FrontEnd
Documentation
English
LibraryResources
Windows
libraries for use on Windows
Windows-x86-64
libraries for use on 64 bit Windows
Linux
libraries for use on Linux
Linux-x86-64
libraries for use on 64 bit Linux
MacOSX-x86
libraries for use on MacOSX
MacOSX-x86-64
libraries for use on 64 bit MacOSX
ライブラリの依存関係
ライブラリが他のライブラリに依存している場合は,その依存しているライブラリが利用できることを確認しておく必要がある.これはこれらの余剰ライブラリをシステムのどこかにインストールするためにWolfram言語が実行される環境を変更するか,またはLD_LIBRARY_PATH (Linux)やPATH (Windows)のようなパス環境変数を変えることで行える.
また、自分のライブラリをロードする前にLibraryLoadを使って依存しているライブラリをロードするという方法もある.LibraryLoadはLibraryFunctionLoadと違って関数を返さない.これは依存しているライブラリをロードするためだけに存在するものである.
Mac OS Xでは,ダイナミックライブラリ(ファイル拡張子はdylib)はより複雑なメカニズムを持っており,依存しているライブラリは特定の場所のみ検索するように設定できる.otoolやinstall_name_tool等のコマンドについて調べる必要がある.
ライブラリのバージョン情報
LibraryLink パッケージを使うと,ライブラリのバージョンについての情報を得ることができる.このパッケージは共有ライブラリを扱うための追加ツールを提供する.
ここでLibraryVersionInformationがライブラリについての情報を表示する規則のリストを返す.
または,次に示すようにLibraryVersionStringを使ってバージョン情報を文字列として得ることもできる.
ライブラリをロードする際の問題
ライブラリのロードで問題がある場合は,$LibraryErrorを使ってロードしない原因について調べてみるとよい.これはLibraryLink`コンテキストの中にあるが,使うためにパッケージをロードする必要はない.
このライブラリは他のライブラリへの依存関係がある.通常Wolfram言語は他のライブラリを先にロードする.しかし暦ライブラリだけを直接ロードしようとしている上,まだ暦関数を使ったことがない場合,このロード操作は次のように失敗に終る.
次のようにして,$LibraryErrorを使ってこのエラーの詳細を知ることができる.
Windowsでは見付けられなかった依存ライブラリの名前を与えないが,他のプラットフォームでは名前が分かる.