2000/06/26    Physics Simulation Package (Physsim-2000a-2)

ようやく、自前の物理解析プログラム(Physsim)が最新版の JSF(jsf-1-14) に対応できた。新しい JSF は C++ で書き直された BASES/SPRING を使っているので古いものと整合性がなかったのだ。モンテカルロイベントジェネレーターがたくさんあるので、その全てについて対応するのは手作業では大変。そのことがやる気をそいでいたのと、他のことで忙しかったこととで今に至ってしまった。このままでは、どんどん傷口が広がりそうなので、ついに観念して移行に踏み切った。ただ、やはり、手作業では大変なので perl スクリプトで行った。

この作業の中で学んだことを忘れないように書いておく。

[1] How to call a C++ function from FORTRAN

実際、BASES/SPRING のヒストグラムパッケージが C++ になってしまったので、これが必須。以下に例を示す。
FORTRAN は例えば以下のようにヒストグラムをフィルするサブルーチン xhfill を呼ぶ。
      subrouitine func(x)
      implicit real*8 (a-h,o-z)
      external xhfill
      .........
      call xhfill('h01', x, w)
      .........
      return
ここで、x はフィルする値、w は重みである。この xhfill の実体を C++ で書かれた JSFBases クラスのメンバー関数(JSFBases::H1Fill)にしたいわけである。
この場合、まず FORTRAN 側の修正は、上のように external xhfill を宣言すること。次に、C++ の側で以下のようにする。
JSFBases *bso;   // これは H1Fill をメンバー関数とする JSFBases オブジェクトへのポインター
extern "C" {     // これでシンボル名がマングルされないので、FORTRAN から見える
void xhfill_(char *t, double *x, double *w, int len) // xhfill の後のアンダースコアー、
{                                                    // 文字列の長さを保持する引数 len に注意。
   char tmp[1024];                                   // また、引数は全てアドレスわたしである。
   int i;
   for (i=0; i<len; i++) tmp[i] = t[i];
   tmp[len] = '\0';         // FORTRAN の文字列はヌルターミネートされていない
   bso->H1Fill(tmp,*x,*w); // 実行時、bso は確定しているのでメンバー関数が呼び出せる
}
}
FORTRAN 関数名にはアンダースコアーがつけられること、引数が全て値でなくアドレスわたしであること、そして FORTRAN の文字列の扱いの特殊性に注意(FORTRAN では文字列はヌルターミネートされておらず、そのかわりに文字列の長さを、FORTRAN では見えない引数でわたす。従って C または C++ にその文字列をわたす際にはヌルターミネートしてから、その長さといっしょにわたす必要がある)。
後は、bso を JSFBases のコンストラクターの中で
jSFBases::JSFBases(......) : .....
{
   .....
   bso = this;
   .....
}
のようにセットする。スタティック関数なら bso->H1Fill(tmp,*x,*w) のようにポインターを使うのでなく、JSFBases::H1Fill(tmp,*x,*w) のように書けばよい。
 
 

[2] How to call a FORTRAN function from C++

これは、上の場合のノウハウの応用である。例えば
      subroutine subfort(i,x,str1,array,str2)
      integer*4 i
      real*4    x
      character*(*) str1, str2
      ......
      return
      end
なる FORTRAN 関数は、
extern "C" {
extern void subfort_(int *i, float *x, char *str1, float array[],
                     char *str2, int len1, int len2);
......
}
などとしておけば良い。もちろん、これを C++ から呼ぶ際にはアドレスわたしであることに注意し、len1 と len2 もセットして呼ばねばならない。
例えば、
int i = 0;
float x =1.;
float array[] = {0., 1., 2.};
char *str1 = ".....";
char *str2 = ".....";
......
subfort_(&i, &x, str1, array, str2, strlen(str1), strlen(str2));
のような感じ。
 

[3] How to access a FORTRAN COMMON from C++

例えば、次のような FORTRAN の COMMON にアクセスしたい場合。
      common /fcom/ i, x, y, a(100)
      integer*4  i
      real*4     x, y, a
まず
extern "C" {
typedef struct {
   int   i;
   float x;
   float y;
   float a[100];
} COMMON_fcom;
}
などと宣言し、
COMMON_fcom fcom_;
後は、C++ のプログラムの中で
fcom_.i = 1;
fcom_.a[10] = 0.5;
float x = fcom_.x;
などとすればよい。
 


Back to Keisuke Fujii's MkLinux/LinuxPPC Life