Wolframコンパイラで外部ライブラリを呼び出す
はじめに
コンパイル型言語(C,C++,Rust,Swift,Haskell等)の多くはC互換性の動的ライブラリをコンパイルすることができる.これらのライブラリの関数はコンパイルされたWolfram言語で直接呼び出すことができるため,トップレベルのWolfram言語と動的ライブラリの間の高性能リンクを書くことが可能になる.
このチュートリアルには,単独のOpenSSL関数への接続の短い例の他,大規模なSQLite機能のインターフェースの拡張された例が含まれている.
OpenSSLとの接続
OpenSSLは暗号化機能を実装するC互換ライブラリを中心としている.この例ではOpenSSLの暗号論的疑似乱数生成器に接続する.
OpenSSLは以下の型のシグネチャを使ってRAND_bytesという関数を公開する:
int RAND_bytes(unsigned char *buf, int num);
第1引数のbufは,それが乱数のバイトを書き込むバッファである.第2引数のnumはbufに書き込むバイト数である.
(OpenSSLはWolfram言語カーネルによってデフォルトでロードされているため,第2引数でライブラリを指定する必要がなかった.)
まず,指定された長さで管理される"CArray"を作成するためにCreateTypeInstanceが使われる.これはランダムなバイトが書き込まれるバッファである.
次にLibraryFunctionDeclarationによって宣言されたOpenSSL関数を呼び出すためにLibraryFunction["RAND_bytes"]が使われる.
最後にCreateTypeInstanceを再び使ってバッファの要素を抽出しそれらを"NumericArray"にコピーする.このタスクにはFromRawPointerを使うこともできる.
SQLiteとの接続
SQLiteはSQLデータベースを実装するC互換ライブラリである.このチュートリアルを実行するためにはSQLiteがパクレットとしてダウンロードされていなければならない.
パクレットはPacletUninstallを実行するとアンインストールすることができる.
SQLiteの文書化されたCインターフェースには以下のシグネチャを持つsqlite3_libversion_numberという関数が含まれている:
int sqlite3_libversion_number(void);
これは単独の関数の呼出しを示しているが,同じメカニズムを使って複雑なカスタムデータ構造を動的ライブラリの間のやり取りをする複雑なリンクを作成することができる.
このチュートリアルはトップレベルのWolfram言語とSQLiteの間の効率的なリンクを作成するのに必要なステップを説明する.まずSQLiteデータベースを開き,自動的に閉じるためのシステムを作成する.それからこれらのデータベースでクエリを実行する.最後にクエリから結果を読み戻し,その後のデータ解析に使えるようにそれをWolfram言語の式に変換する.
データベースを開き閉じる
SQLiteデータベースに接続するための第一歩は,それを開き閉じることである.これは2つの関数で行うことができ,以下のシグネチャを持つ:
int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
int sqlite3_close(sqlite3*);
この"Managed"オブジェクトにはコンパイルされたコードとトップレベルのコードの両方における共有参照カウントがあり,その参照カウントが0になると解放コードが実行される.つまりトップレベルでSQLiteデータベースオブジェクトを送り合ってもメモリ的に安全ということである.
簡単なクエリ
データベースを開いたり(自動的に)閉じたりできるようになったので,クエリを実行してみる.SQLiteの最も簡単なクエリインターフェースはsqlite3_execを介したものである:
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
これはエラーコード0を返す.表が作成できたということを意味する.
このexecquery関数はデータベース上で任意のクエリが実行できるが,クエリの結果を読み込む,または便利な形式で返すためのメカニズムはない.次のセクションではこれについて説明する.
読込み機能を加える
クエリ結果の走査
クエリ結果から読み込むためには,前とは少し異なるインターフェースを使う.sqlite3_execを使う代りに,sqlite3_prepareを使ってクエリ文を生成し,sqlite3_stepを使って結果に踏み込む.クエリ文を終了するためにsqlite3_finalizeも必要である.これらの型のシグネチャは次の通りである:
int sqlite3_prepare(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
int sqlite3_step(sqlite3_stmt*);
int sqlite3_finalize(sqlite3_stmt *pStmt);
行を式に変換する
上の関数により,クエリを実行し結果を走査することができる.しかし各行からデータを読み込みそれをWolfram言語式に変換するためにはライブラリ関数がもっと必要になる.特にこれらのSQLite関数は列数とその名前,型を取り出すのに使われる.
int sqlite3_column_count(sqlite3_stmt *pStmt);
const char *sqlite3_column_name(sqlite3_stmt*, int N);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
これらのSQLite関数は各行のさまざまな型のデータを抽出するために使われる:
double sqlite3_column_double(sqlite3_stmt*, int iCol);
sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
クエリ関数のコンパイル
openDBを使ってデータベースを開き自動的に閉じることができるようになったので,createStatementでクエリをコンパイルすることができる.クエリ結果の各行はrowAssociationで式に変換することができる.後はそれをまとめるだけである.