C言語でのWSTP開発(Linux)

このドキュメントでは,Linuxシステム上で,C言語で書かれたWolfram Symbolic Transfer Protocol (WSTP)プログラムをコンパイルし実行する方法を説明する.(「WSTPと外部プログラム通信」はWolfram言語とC言語の両方でWSTPプログラムを書く方法を記述したものである.)
このドキュメントは,コンパイラや他の開発ツールの使い方を教えたり,C言語でのプログラミング方法を教えたりするものではない.WSTPプログラムの構築や実行で問題がある場合は,このドキュメントの「トラブルシューティング」をご覧いただきたい.
このドキュメントで記述されていることの多くは,Linux独特のものであり,サポートされるすべてのLinuxプラットフォームに当てはまる.別のプラットフォームでWSTPプログラムをコンパイルし実行する方法については,そのプラットフォームの開発者ガイドをご参照いただきたい.
サポートされる開発プラットフォーム
WSTPは共有ライブラリとして,標準の呼出し慣習,および以下にリストされたコンパイラにより指定されたバイナリインターフェースに従ったどの開発環境とでも使うことができる.
以下のコンパイラの中には,そのコンパイラの製造元により製造された統合開発環境と統合するものもあるが,makeユーティリティを使っても同様にうまく機能する.
Cコンパイラ
C++コンパイラ
"Linux"
gcc (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
g++ (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
"Linux-x86-64"
gcc (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
g++ (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
上記に加え,WSTP APIをコンパイルするにはlibuuid開発ライブラリが必要である.これはDebianシステムではuuid-dev,Red HatとSuseではlibuuid-develと呼ばれる.
WSTPコンポーネントのインストール
WSTP Developer Kit (WSDK)は,Wolframシステムディレクトリの$InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/$SystemIDディレクトリにある.

推奨されるインストール

CompilerAdditionsのインストール

WSTPプログラムを構築するのに必要なWSTPコンポーネントは,Wolframシステムのインストーラですでにインストールされている.これらのコンポーネントを使う方法の一つとして,これらをWolframシステムのディレクトリに残したままにしておき,コンパイラを呼び出すときにそのコンポーネントの完全パス名を指定するというものがある.「WSTPプログラムの構築」セクションの例題「makefiles」ではこのアプローチを使う.
別の方法として,コンパイラがこれらのフコンポーネント(wstp.h,libWSTP32i4.a,libWSTP32i4.so,libWSTP64i4.a,libWSTP64i4.so)を自動的に検索するディレクトリにコンポーネントをコピーするというものがある.このディレクトリは通常/usr/includeおよび/usr/libであるが,システムにより異なる可能性がある.多くのシステムでは,すべてのユーザにこれらのディレクトリへの書込み許可があるとは限らない.

WSTPExamplesのインストール

WSTPExamplesディレクトリをホームディレクトリにコピーする.

WSTPフレームワークコンポーネント

以下はWSDKに含まれるそれぞれのファイルおよびディレクトリの説明である.

CompilerAdditionsディレクトリ

wstp.h
wstp.hはCあるいはC++のソースファイルに含まれなければならないヘッダファイルであり,コンパイラによって見付けられる場所になければならない.このヘッダファイルは,ソースファイルと同じディレクトリにコピーすることも,標準ヘッダファイルと同じ場所にコピーすることもできる.また,ヘッダファイルの検索パスにWSTPディレクトリを加えた場合はそのままの場所に置いておくこともできる.
libWSTP32i4.a/libWSTP64i4.a
これはWSTP関数すべてを含む静的ライブラリであり,プロジェクトに含まれていなければならない.このライブラリはソースファイルを同じディレクトリにコピーすることができる.また,ライブラリの検索パスにWSTPディレクトリを加えた場合は,そのままの場所に置いておくこともできる.32/64はWSTPライブラリが32ビットバージョンか64ビットバージョンかを示している.
libWSTP32i4.so/libWSTP64i4.so
これはWSTP関数すべてを含む動的共有ライブラリであり,プロジェクトに含まれていなければならない.このライブラリはソースファイルを同じディレクトリ,あるいは/libや/usr/libというシステム規模の場所にコピーすることができる.また,ライブラリの検索パスにWSTPディレクトリを加えた場合は,そのままの場所に置いておくこともできる.32/64はWSTPライブラリが32ビットバージョンか64ビットバージョンかを示している.
wsprep
wsprepテンプレートファイルを処理することにより自動的にWSTPプログラムを書くアプリケーションである.このアプリケーションは,プロジェクトと同じディレクトリにコピーするか,それへのエイリアスを作っておくと便利である.
wscc
wsccはWSTPソースファイルを前処理しコンパイルするスクリプトである.

WSTPExamplesディレクトリ

このディレクトリには非常に簡単ないくつかのWSTPプログラムのソースコードが含まれている.このソースコードを使うことによって,自分でコードを書かずにWSTPプログラムを構築して実行する方法が学べる.

PrebuiltExamplesフォルダ

このフォルダにはすでに構築された例題プログラムが含まれている.「WSTPプログラムの実行」ではそのプログラムのうちの2つを実行する方法を説明する.「WSTPプログラムの構築」ではWSTPExamplesフォルダのソースコードを使って自分でプログラムを構築する方法を説明する.
WSTPプログラムの構築
WSTPプログラムを構築する一般的な手順は,WSTP関数を呼び出すCあるいはC++のソースファイルにwstp.hを加え,ソースファイルをコンパイルし,結果のオブジェクトコードをlibWSTP32i4.a,libWSTP64i4.a,libWSTP32i4.so,libWSTP64i4.soライブラリか,アプリケーションによって必要とされる他の標準ライブラリに連結するというものである.アプリケーションがWSTPテンプレートメカニズムを使っている場合,テンプレートファイルはまずwsprepを使ってCのソースファイルに処理されなければならない.

WSTPのテンプレートファイルを使う

「WSTPと外部プログラム通信」で説明してあるようにプログラムでWSTPテンプレートメカニズムを使う場合は,wsprepアプリケーションを使ってテンプレートエントリを含むソースファイルを同時に前処理しなければならない.テンプレートエントリは,テンプレートキーワードを含む行の列である.各エントリは,評価されたときに,関連付けられたC関数を呼び出すWolfram言語関数を定義する.wsprepがそのようなソースファイルを処理するとき,テンプレートエントリをC関数に変換し,他のテキストを変更せずに渡し,WSTPを使ってリモート手続き呼び出しメカニズムを実装する追加のC関数を書き出す.結果はコンパイルの準備ができたCのソースファイルである.
例えば,以下のコマンド
は,addtwo.tmのテンプレートエントリと他のテキストからCのソースファイルaddtwotm.cを生成する.それからCコンパイラを使って出力ファイルをコンパイルする.プログラムの構築にmakeユーティリティを使っている場合は,makefileに以下の規則と類似の規則を加えることができる.

WSTPプログラムを構築する

次は,addtwofactorを含むWSDKのサンプルプログラムを構築するのに必要な,サンプルのmakefileである.サンプルプログラム(この場合はaddtwo)を構築するためにはWSTPExamplesディレクトリで以下のコマンドを評価する.

Makefileを使う

# This makefile can be used to build all or some of the sample
# programs. To build all of them, use the command
# 'make all'. To build one, say addtwo, use the command
# 'make addtwo'.

WSTPLINKDIR = /usr/local/Wolfram/Mathematica/10.0/SystemFiles/Links/WSTP/DeveloperKit
SYS = Linux # Set this value with the result of evaluating $SystemID
CADDSDIR = ${WSTPLINKDIR}/${SYS}/CompilerAdditions

INCDIR = ${CADDSDIR}
LIBDIR = ${CADDSDIR}

EXTRALIBS = -lm -lpthread -lrt -lstdc++ -ldl -libuuid # Set these with appropriate libs for your system.
WSTPLIB = WSTP32i4 # Set this to WSTP64i4 if using a 64-bit system

WSPREP = ${CADDSDIR}/wsprep

all : addtwo bitops counter factor factor2 factor3 quotient reverse sumalist

addtwo : addtwotm.o addtwo.o
    ${CC} -I${INCDIR} addtwotm.o addtwo.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

bitops : bitopstm.o bitops.o
    ${CC} -I${INCDIR} bitopstm.o bitops.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

counter : countertm.o
    ${CC} -I${INCDIR} countertm.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

factor : factor.o
    ${CC} -I${INCDIR} factor.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

factor2 : factor2.o
    ${CC} -I${INCDIR} factor2.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

factor3 : factor3.o
    ${CC} -I${INCDIR} factor3.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

quotient : quotient.o
    ${CC} -I${INCDIR} quotient.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

reverse : reversetm.o
    ${CC} -I${INCDIR} reversetm.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

sumalist : sumalisttm.o sumalist.o
    ${CC} -I${INCDIR} sumalisttm.o sumalist.o -L${LIBDIR} -l${WSTPLIB} ${EXTRALIBS} -o $@

.c.o :
    ${CC} -c -I${INCDIR} $<

addtwotm.c : addtwo.tm
    ${WSPREP} $? -o $@

bitopstm.c : bitops.tm
    ${WSPREP} $? -o $@

countertm.c : counter.tm
    ${WSPREP} $? -o $@

reversetm.c : reverse.tm
    ${WSPREP} $? -o $@

sumalisttm.c : sumalist.tm
    ${WSPREP} $? -o $@

以下の表を使うと,使用中のシステム上でWSTPプログラムの接続に必要な追加のライブラリが判別できる.

$SystemID
EXTRALIBS
"Linux"
-lm -lpthread -lrt -lstdc++ -ldl -luuid
"Linux-x86-64"
-lm -lpthread -lrt -lstdc++ -ldl -luuid

wsccを使う

wsccはWSTPソースファイルを前処理してコンパイルするスクリプトである.これはファイル名が.tmで終わるファイルすべてのWSTPテンプレートを前処理して,その結果のCソースコードに対してccを呼ぶ.wsccはコマンドラインオプションと他のファイルを直接ccに渡す.次がwsccを使ってaddtwoアプリケーションを構築するコマンドである.
wscc addtwo.tm addtwo.c -o addtwo
WSTPプログラムの実行
「WSTPプログラムの構築」の手順は,WSTPExamplesディレクトリのソースコードを使って2つのWSTPプログラムを構築する方法を説明するものである.これらの2つのプログラムaddtwofactorはすでにPrebuiltExamplesフォルダの中に構築されている.自分で構築する前にすでに構築された例題を実行してみて,WSTPシステム追加分がインストールされ正常に動いていることを確認し,プログラムが適切に構築されるとどのようなことが起こるかを学んでおくとよい.
WSTPプログラムにはaddtwoプログラムで要約されるものとfactorプログラムで要約されるものの2つの基本的な形式がある.addtwoプログラムで要約されるものはインストール可能なプログラムである.インストール可能なプログラムは,呼出しメカニズムを介してCプログラムをカーネルに接続することにより,カーネルに新しい機能を提供する.この新しい機能を取得するためには,Wolfram言語のユーザはInstall[]関数を実行しなければならない.addtwoの例題では,引数として提供される2つの数を足すAddTwo[]と呼ばれる新しい関数を加えることになる.カーネルとインストール可能プログラムは互いにのみ通信し合える特別な関係を持つ.インストール可能プログラムが実行されるとき,接続のために情報を提供する必要がある.factorプログラムで要約されるものは,フロントエンドである.フロントエンドはリンクを作成したり管理したりする作業をすべて行う.factorの例題に加え,WolframシステムフロントエンドとWolfram言語カーネルもフロントエンドタイプの例である.フロントエンドは実行するために追加の情報を必要としないが,通常実行中のある段階で接続を確立する.

Wolfram言語カーネルからすでに構築された例題を実行する

最初の例題プログラム,addtwoはWolfram言語にインストールされているWSTPテンプレートプログラムである.つまり,このプログラムは背景で実行し,Wolfram言語のサービスとして,外部でコンパイルされた1つあるいは複数の関数を提供する.Wolfram言語ユーザにとって,これらの関数は組込みのように見える.addtwoプログラムは,C関数のaddtwo()の呼出しとしてWolfram言語関数AddTwo[]を定義するテンプレートファイルを使う.テンプレートメカニズムについては「WSTPと外部プログラム通信」で説明してある.このプログラムのソースコードは以下のようなものである.
パスの文字列を編集して,以下の2つのセルを評価する:
新しく利用可能になった関数を見るためには,次のセルを評価する:
以下でファイルaddtwo.tmで定義されているAddTwo[]関数の使用法メッセージを表示する:
使ってみる:
2つの機械整数の和が機械整数に適合しない場合,あるいはどちらかの引数が機械整数でない場合にどうなるかを見てみる(2^31-1は最大の機械整数である.コンパイラが2バイト整数を使う場合は2^15-1が最大のC intである):
addtwoプログラムは大きい整数には適していない.
以下はAddTwo[_Integer, _Integer]ではない:
Install[]LinkOpen[]を呼び出し,外部プログラムと情報を交換してAddTwo[]の定義を設定する.この詳細について心配する必要はないが,興味がある方は以下を評価するとよい:
外部プログラムを使い終わったら,以下を評価する:

すでに構築された例題内からWolfram言語カーネルを起動する

2つ目の例題プログラムのfactorは,Wolfram言語カーネルが背景で動いており,factorのサービスとしてカーネルの計算サービス(この場合はユーザによって入力された整数を因数分解する機能)を提供するという意味で,Wolframシステムのフロントエンドであると言える.
以下のコマンドを実行してfactorアプリケーションを起動する.
しばらくするとプロンプトが現れ,整数を入力するよう求める.10桁未満の整数をタイプしEnterを押す(他のfactor例題はタイプする整数の大きさの制約が緩い).
Wolfram言語により返された素因数が出力され,factorはWolfram言語とのリンクを閉じる.

サポートされるリンクプロトコル

C関数のWSOpenArgcArgv(),およびWolfram言語関数のLinkOpen[]については,「WSTPと外部プログラム通信」で説明してある.LinuxマシンではLinkProtocolオプションの適正な値は"TCPIP""TCP""SharedMemory""Pipes""IntraProcess"である.LinkProtocolは接続の一端から他方へとデータを転送するのに使われるメカニズムである.LinkMode->Launchリンクのデフォルトのプロトコルは"SharedMemory"である."SharedMemory"LinkMode->ListenリンクおよびLinkMode->Connectリンクのデフォルトでもある.
"TCPIP"プロトコルと"TCP"プロトコルでは,リンク名は符号のない16ビットの整数である. "TCPIP"のリンク名が整数であっても,WSOpenArgcArgv()およびLinkOpen[]への桁の列として与えられる.
トラブルシューティング