4.1. Example 1
単純な例 – Y 0 (x) ベッセル関数ルーチン s17acc |
内容
- nAG C Library マニュアルからの関数プロトタイプ
- Javaプログラム中でのネイティブ関数の宣言
- インタフェースライブラリのロード
- Javaプログラムのコンパイル
- C用のヘッダファイルの生成
- ネイティブ関数のCによる実装
- 共用ライブラリ/DLLの作成
- プログラムの実行
- クイックサマリ
- Javaプログラム中でのネイティブ関数の宣言
- インタフェースライブラリのロード 先に議論したように、Javaと nAG C Library 間を媒介するためのネイティブメソッドを含む共用ライブラリを構築する必要がある。Javaプログラムがやらなくてはならないもう一つのことは、nagCJavaInterface と名付けたインタフェースライブラリをロードすることである。そのためにはその名称を Java System.LoadLibrary() コールで引き渡せば良い。インタフェースライブラリはOSによって若干異なる名前を持つこともあるが、LoadLibrary コールがそれを解決してくれる。例えばSolaris環境の場合、Javaコード
- Javaプログラムのコンパイル 次に示すのはJavaプログラム Bessel.java のソースコードであり、その中にはネイティブメソッドの宣言と LoadLibrary コールを含む。
- C用のヘッダファイルの生成 Bessel.java のコンパイルが終わると、javah ツール(JDK中に含まれている)を使ってCコンパイラが使用するヘッダファイルを作成することができる。そのためのコマンドは次のとおり。
- ヘッダファイル <jni.h> を含む。これはJDK中に含まれている。
- Cコンパイラから見えるネイティブ関数の宣言は次のようなものとなる。
JNIEXPORT jdouble JNICALL Java_Bessel_s17acc (JNIEnv *, jobject, jdouble);
この関数の名称は Java_Bessel_s17acc であり、それが宣言されているJavaクラスを示している。
JNIEXPORT と JNICALL は <jni.h> を介して定義される。これらはいくつかのシステムにおける呼出し規約を変更するのに用いられるが、ここでは気にする必要はない。
- 型 JNIEnv, jobject, jdouble もまた <jni.h> を介して定義される。これらはマシン依存のCタイプを用いて定義される。例えば jdouble はJavaの倍精度型であり、Cの倍精度型に等値される。
- Cの視点からすると、我々の関数は1つではなく3つの引数を有する。JNIEnv* と jobject という型の最初の2つの引数によってCコードからJava環境へのアクセスが可能になる。当面の単純なケースにおいては第3の引数のみが関心の対象となる。すなわち nAG C Library に引き渡されることになる Y0 (x) ベッセル関数に対する引数 x である。
- ネイティブ関数のCによる実装 ヘッダファイル Bessel.h が作成できたところで、今度は Java_Bessel_s17acc のCコードによる実装に移る。次はファイル BesselImp.c の内容を記したものである。
- 適切な nAG C Library ヘッダファイル <nag.h>, <nags.h> をインクルードする必要がある。
- 変数 fail を型 NagError として宣言する。nAG C Library マニュアル上で推奨されているように、fail を静的変数として設定する。これによって初期化は不要となる。
- s17acc に対するコールの戻り値はそのままJavaの呼出し元に引渡す。
- 共用ライブラリ/DLLの作成 このステップはOS依存となる。
- Linux上でのコンパイル
最初に BesselImp.c をコンパイルする。
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \ -I/opt/nAG/cll6a09dgl/include BesselImp.c
-I スイッチはCコンパイラに対し、ヘッダファイル探索位置を指示する。第1のディレクトリ /opt/jdk1.6.0_11/include は jni.h ヘッダファイルの探索用に用いられる。第2のディレクトリ /opt/jdk1.6.0_11/include/linux はマシン依存で、型の定義を見出す上で jni.h によって必要とされる。少なくとも linux という部分はマシンのタイプによって変化する。第3と第4のインクルードディレクトリはシステム上にインストールされている nAG C Library ヘッダファイルの位置を示すものでなくてはならない。
BesselImp.c が正常にコンパイルされたら、次のコマンドによってそれを共用オブジェクト化する。
% ld -G -z defs BesselImp.o -o libnagCJavaInterface.so \ /opt/nAG/cll6a09dgl/lib/libnagc_nag.so -lm -lc -lpthread
-G フラグは共用オブジェクトの作成を意味する。-z defs フラグはすべてのシンボルがリンク時に解決されていないのであればリンク失敗を意味する。このフラグは厳密に言えば必要ないのであるが、"failed to load library" というとんでもないJava実行時メッセージを回避するのに有効である(とんでもないという意味は、問題が他にあるにもかかわらずメッセージによって libnagCJavaInterface.so が名指しされてしまうからである)。-o フラグは共用ライブラリの名称を、Javaコード内の LoadLibrary() コールによって必要とされる libnagCJavaInterface.so と規定する。最後に -lm と -lc フラグは必要なシステムの数学ライブラリ、C実行時ライブラリとのリンクを確実なものとする。
注意 他のUNIX系マシン上では、OSとか使用される nAG Library のバージョンによって、さらなるライブラリの追加がリンク時に必要となる場合がある。
- Windows上でのVisual C++によるコンパイル
コンパイルとDLLの作成を1ステップで行う。
C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 -I"c:\Program Files\nAG\cldll094zl\include" /Gz -LD BesselImp.c "c:\Program Files\cldll094zl\lib\CLDLL094Z_nag.lib" -FenagCJavaInterface.dll
UNIX環境下の場合と同様、3つの -I スイッチはCコンパイラに対し、どこでヘッダファイルの探索を行えば良いかを指示する。第1のディレクトリ c:\jdk1.6.0_11\include は jni.h ヘッダファイルの探索用に用いられる。第2のディレクトリ c:\jdk1.6.0_11\include\win32 は、型の定義を見出す上で jni.h によって使用されるディレクトリである。第3のインクルードディレクトリ "c:\Program Files\nAG\cldll094zl\include" はシステム上にインストールされている nAG C Library ヘッダファイルの位置を示すものでなくてはならない。"c:\Program Files\cldll094zl\lib\CLDLL094Z_nag.lib" はシステム上にインストールされている nAG C Library の位置を示す。/Gz コンパイラオプション(__stdcall 呼出し規約を使用すること)は重要である。それを指定しなかった場合、コンパイルとリンクは正常に行われプログラムの実行開始にも至るが、アクセス違反が生じることになる。-LD フラグは“DLLの作成”を意味する。-Fe フラグは出力ファイルを nagCJavaInterface.dll と指定する。 - プログラムの実行 これまでの操作がすべて正常に行われたとすると、次のコマンドによってプログラムを実行できる。
- Linuxマシン
環境変数の名前は LD_LIBRARY_PATH であり、libnagCJavaInterface.so と libnagc_nag.so をサーチするためのコロンで区切られたディレクトリリストを含む。例えばCシェルを使用する場合には、コマンド% setenv LD_LIBRARY_PATH .:/opt/nAG/cll6a09dgl/lib
と指定することによって現行ディレクトリ (.) とディレクトリ /opt/nAG/cll6a09dgl がサーチされるようになる。 - 他のUNIXマシン
環境変数については ld コマンド、もしくはそこから参照されるコマンドのマニュアルページに記述されているはずである。共通的な名称は LD_LIBRARY_PATH と SHLIB_PATH である。 - Windows
DLLの位置を特定するためだけの専用の環境変数が存在するわけではない。PATH 変数がサーチされるので、CLDLL094Z_nag.dll と nagCJavaInterface.dll はそのPATH中のどこかに存在していなくてはならない。 - クイックサマリ 2つのソースファイル Bessel.java と BesselImp.c が与えられたとして、次のコマンドを発行する。
- Javaクラスのコンパイル:
% javac Bessel.java
- ヘッダファイルの作成:
% javah -jni Bessel
- インタフェースライブラリのコンパイル:
- (Linux x86)
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \ -I/opt/nAG/cll6a09dgl/include BesselImp.c % ld -G -z defs BesselImp.o -o libnagCJavaInterface.so \ /opt/nAG/cll6a09dgl/lib/libnagc_nag.so -lm -lc -lpthread
ただしディレクトリ名称 /opt/jdk1.6.0_11/include, /opt/jdk1.6.0_11/include/linux, /opt/nAG/cll6a09dgl/include, /opt/nAG/cll6a09dgl/lib はJavaと nAG C Library のインストレーションに合せて適宜調整を要す。 - (Windows/Visual C++)
C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 -I"c:\Program Files\nAG\cldll094zl\include" /Gz -LD BesselImp.c "c:\Program Files\cldll094zl\lib\CLDLL094Z_nag.lib" -FenagCJavaInterface.
ただしディレクトリ名称 c:\jdk1.6.0_11\include, c:\jdk1.6.0_11\include\win32, "c:\Program Files\nAG\cldll094zl\include", "c:\Program Files\nAG\cldll094zl\lib" はJavaと nAG C Library のインストレーションに合せて適宜調整を要す。
- (Linux x86)
- Javaプログラムの実行:
% java Bessel 1.0
nAG C Library マニュアルからの関数プロトタイプ
C Library マニュアルによれば関数 s17acc に対するプロトタイプは次のとおりである。
#include <nag.h> #include <nags.h> double s17acc(double x, NagError *fail);この関数は2つの引数を取る。第1の引数は型が倍精度(double)で、ベッセル関数 Y0 (x) に対する引数 x を意味する。s17acc への第2の引数はエラー処理用の引数 fail で、その型は NagError である。NagError タイプに関する詳細情報については nAG C Library マニュアルを参照されたい。それによって s17acc が正常に実行されたか否かに関するフィードバックが得られるという点を除き、それ以上の内容についてここでは立ち入らなくて良い。
本用例においては物事を極力簡単化するために、NagError 構造体の内容をJavaには戻さないものとする。従ってJavaプログラム内では関数を次のように宣言すれば良い。
// Declaration of the Native (C) function private native double s17acc(double x);これは double の引数を取り、double を応答として返すメソッドを意味する。native というキーワードはJavaコンパイラに対し、そのメソッドがJavaの外部で実装されるものであることを示す。
System.loadLibrary("nagCJavaInterface");は "libnagCJavaInterface.so" という名前のライブラリをサーチするが、Windows環境の場合には "nagCJavaInterface.dll" という名前のライブラリをサーチする。
public class Bessel { // Declaration of the Native (C) function private native double s17acc(double x); static { // The runtime system executes a class's static // initializer when it loads the class. System.loadLibrary("nagCJavaInterface"); } // The main program public static void main(String[] args) { double x, y; int i; /* Check that we've been given an argument */ if (args.length != 1) { System.out.println("Usage: java Bessel x"); System.out.println(" Computes Y0 Bessel function of argument x"); System.exit(1); } // Create an object of class Bessel Bessel bess = new Bessel(); /* Convert the command line argument to a double */ x = new Double(args[0]).doubleValue(); System.out.println(); System.out.println("Calls of nAG Y0 Bessel function routine s17acc"); for (i = 0; i < 10; i++) { /* Call method s17acc of object bess */ y = bess.s17acc(x); System.out.println("Y0(" + x + ") is " + y); /* Increase x and repeat */ x = x + 0.25; } } }
メインプログラムではコマンド行から x の値を取り込み、その引数、及びそれから導かれる9つの引数を使ってネイティブメソッドをコールする。
これで関数 s17acc に対するネイティブな宣言を含むJavaプログラムができ上がったので、次のコマンドによってコンパイルを行う。
% javac Bessel.java
問題がなければコンパイラによって Bessel.class という名前のファイルが生成されているはずである。
% javah -jni Bessel
このコマンドにより Bessel.h という名前の次のようなファイルが生成される。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Bessel */ #ifndef _Included_Bessel #define _Included_Bessel #ifdef __cplusplus extern "C" { #endif /* * Class: Bessel * Method: s17acc * Signature: (D)D */ JNIEXPORT jdouble JNICALL Java_Bessel_s17acc (JNIEnv *, jobject, jdouble); #ifdef __cplusplus } #endif #endif このヘッダファイルに関する注意事項:
#include <jni.h> /* Java Native Interface headers */ #include "Bessel.h" /* Auto-generated header created by javah -jni */ #include <nag.h> /* nAG C Library headers */ #include <nags.h> /* Our C definition of the function s17acc declared in Bessel.java */ JNIEXPORT jdouble JNICALL Java_Bessel_s17acc(JNIEnv *env, jobject obj, jdouble x) { double y = 0.0; static NagError fail; /* Tell the routine we want no output messages on failure */ fail.print = Nag_FALSE; /* Call the Y0(x) Bessel function s17acc */ y = s17acc(x, &fail); if (fail.code != 0) printf("Error: s17acc returned fail code %d for argument %g\n", fail.code, x); return y; }注意事項:
% java Bessel 1.0期待される出力は次のとおり。
Calls of nAG Y0 Bessel function routine s17acc Y0(1.0) is 0.08825696421567678 Y0(1.25) is 0.25821685159454094 Y0(1.5) is 0.38244892379775886 Y0(1.75) is 0.4654926286469062 Y0(2.0) is 0.510375672649745 Y0(2.25) is 0.5200647624572783 Y0(2.5) is 0.4980703596152319 Y0(2.75) is 0.44865872156913167 Y0(3.0) is 0.37685001001279034 Y0(3.25) is 0.288286902673087
ヒント: インタフェースライブラリ nagCJavaInterface が見つからない、あるいは nAG C Library が見つからないというJavaのエラーメッセージが出力された場合には、システムに対してその所在を示す環境変数の設定が必要となることがある。環境変数の名前はOSによって異なる。
Copyright 2009 Numerical Algorithms Group
Page last updated 2009-04-21 14:34:48 mick
[NP3671]