HOME Sather を試そう download 書き込む

15. 他言語 (C, Fortran) とのインターフェース


1. はじめに

今回は Fortran や C 言語とのインターフェースについて述べます。 Fortran や C で書いたほうが実行速度面で有利な場合が多々あるので、 このような方法が用意されているのは Sather が実用的な言語を目指して開発されたことの表れです。

2. Fortran とのインターフェース

階乗の計算と行列の積の2つの例を説明します。

2.1. 階乗の計算

コード(ffact.f, ffact.sa) を [code 1a], [code 1b] に示します。 ffact.f は n の階乗を返す Fortran で書かれた関数です。一方、ffact.sa は ffact.f を呼び出す Sather のプログラムです。

[code 1a]: ffact.f

01:           integer function factorial(n)
02:           integer n
03:           factorial = 1
04:           do i=1,n
05:              factorial = factorial * i
06:           enddo
07:           return
08:           end
[code 1b]: ffact.sa
01:     -- interface with fortran
02:     -- to compile:
03:     -- sacomp ffact.sa -external FACT ffact.f  -o ffact
04:     
05:     
06:     external FORTRAN class FACT is          -- Fortran で書かれた外部クラスを宣言します。
07:        factorial(i:F_INTEGER):F_INTEGER;    -- FACT で定義されている関数です。
08:     end;
09:     
10:     class MAIN is
11:        main(av:ARRAY{STR}) is
12:           i:F_INTEGER := #(av[1].cursor.get_int);  -- Fortran の integer 型は Sather から見ると F_INTEGER class になる。
13:           a:F_INTEGER := FACT::factorial(i);       -- #(INT) でインスタンスを生成できる。
14:           #OUT + av[1] + "! = " + a.str + "\n";    -- str メソッドで文字列に変換する。
15:        end;
16:     end;

コンパイルするときは -external オプションを使ってクラス FACT の実態が ffact.f だということを sacomp に知らせる必要があります。

$ sacomp ffact.sa -external FACT ffact.f -o ffact
$ ./ffact 10
10! = 3628800
Fortran のデータタイプは F_INTEGER, F_REAL などがあります。対応する Sather のデータを引数にして create を呼び出せば、インスタンスが生成されます。 詳しくは Sather — A Language Tutorial: Chapter 13 Interfacing with Fortran をみてください。

2.2. 行列の積

行列を表すには F_ARRAY2{F_data_type} クラスを用います。 F_ARRAY2{F_data_type} は ARRAY2{T} から作ることができます。

行列の積を求めるコードは [code 2a], [code 2b] のようになります。 Fortran と Sather は2次元配列のインデックスの順番が逆になるので注意が必要です。

[code 2a]: mat.f

01:     c
02:     c     calculating the product of matrices c = a * b
03:     c
04:           subroutine mattimes(l,m,n,a,b,c)
05:           integer l,m,n,i,j,k
06:           real a(n,l), b(m,n), c(m,l), tmp
07:           do i=1,l
08:              do j=1,m
09:                 tmp=0.0
10:                 do k=1,n
11:                    tmp=tmp+a(k,i)*b(j,k)
12:                 enddo
13:                 c(j,i)=tmp
14:              enddo
15:           enddo
16:           return
17:           end
[code 2b]: mat.sa
01:     -- Matrix test
02:     --  sacomp mat.sa -chk -external FMAT mat.f -o mat
03:     
04:     external FORTRAN class FMAT is
05:        mattimes(l,m,n:F_INTEGER, a,b,c:F_ARRAY2{F_REAL}); 
06:     end;
07:     
08:     class MAT is
09:        include ARRAY2{FLT};               -- ARRAY2{FLT} に
10:     
11:        times(b:SAME):SAME                 -- 掛け算を定義した MAT クラスを定義する。
12:           pre self.nc = b.nr              -- 掛け算の左側の行列の行数と右側の行列の列数が等しいことを確認
13:        is
14:           c:SAME:=#(self.nr, b.nc);       -- 行数と列数を指定して答えを格納する MAT を宣言
15:           FMAT::mattimes(#(self.nr), #(b.nc), #(self.nc), #(self), #(b), #(c)); -- Fortran で書かれた mattimes を呼び出す。
16:           return c;  -- F_ARRAY2{REAL} の値が変化すると ARRAY2{FLT} の値も変化する。
17:        end;
18:     end;
19:     
20:     class MAIN is
21:        main is
22:           i:INT;
23:           a,b,c:MAT;
24:           a:=#(||1.0, 1.0, 2.0|, |2.0, 2.0, 3.0||);
25:           b:=#(||1.0, 1.0|, |1.0, 2.0|, |1.0, 3.0||);
26:           c:=a*b;         -- 行列の掛け算がきわめて簡単になる。
27:     
28:           loop                          -- 答えの表示
29:     	 i:=1.up!;
30:     	 #OUT + c.elt!;
31:     	 if i%c.nc=0 then #OUT+"\n";
32:     	 else #OUT+" ";
33:     	 end;
34:           end;
35:        end;
36:     end;
mat.sa ではテストとして

を計算しています。
$ sacomp mat.sa -chk  -external FMAT mat.f -o mat
$ ./mat
4 9
7 15

3. C とのインターフェース

Sather と C とのインターフェーシングも Fortran の場合と同様に行えます。

3.1. 階乗の計算

手始めに C を使って階乗を計算してみます。コードは [code 3a], [code 3b] のようになります。

[code 3a]: cfact.c

01:     unsigned long factorial(unsigned long i){
02:         unsigned long j, n;
03:         for(n=1,j=1;j<=i;++j) n*=j;
04:         return n;
05:     }
[code 3b] cfact.sa
01:     -- interfacing with C
02:     -- to compile:
03:     -- sacomp cfact.sa -external FACT cfact.c  -o cfact
04:     --
05:     
06:     external C class FACT is
07:        factorial(i:C_UNSIGNED_LONG):C_UNSIGNED_LONG;
08:     end;
09:     
10:     class MAIN is
11:        main(av:ARRAY{STR}) is
12:           i:C_UNSIGNED_LONG := #(av[1].cursor.get_int);
13:           a:C_UNSIGNED_LONG := FACT::factorial(i);
14:           #OUT + av[1] + "! = " + a.str + "\n";
15:        end;
16:     end;
Sather から C 言語のデータ型を見るときは C_CHAR, C_FLOAT などのクラスを使います。 対応する Sather のデータを引数にして create を呼び出せば、インスタンスが生成されます。 詳しくは Sather — A Language Tutorial: Chapter 14 Interfacing with ANSI C をみてください。

Fortran で書いた ffact と同様にコンパイルして実行します。

$ sacomp cfact.sa -external FACT cfact.c -o cfact
$ ./cfact 10
10! = 3628800

3.2. 配列とポインターとの変換

次に C 言語のポインターを使ったプログラムの例を示します。Sather の ARRAY{T} クラスは array_ptr メソッドに よってポインターに変換されます。

例として、以前紹介した暗号化プログラム crypt.sa の改良版を示します。 Sather は CHAR 型のビット排他論理和が取れないので、CHAR 型を一度 INT 型に変換する必要がありました。 C で書かれた関数を呼び出せばこの部分の効率がよくなります。[code 4a] に C で書かれた関数を、 [code 4b] に crypt.sa の変更した部分を挙げます。

[code 4a]: encrypt.c

01:     void encrypt(char* key, char* plain, char* cipher){
02:         char *key1, *plain1, *cipher1;
03:         for(key1=key, plain1=plain, cipher1=cipher; *plain1 != 0; key1++, plain1++, cipher1++){  // 文字列の最後がヌル文字であることをあてにしている
04:     	if(*key1==0) key1=key;    // key を使い切ったら最初に戻る
05:     	*cipher1 = *key1 ^ *plain1;  // bit ごとの XOR をとる
06:         }
07:         *cipher1=0;  // 最後にヌル文字を追加
08:     } 
[code 4b] crypt.sa (一部)
01:     external C class ENCRYPT is                     -- 外部クラス ENCRYPT を宣言する
02:        encrypt(key,plain,cipher:C_CHAR_PTR);
03:     end;
04:     
05:     class ARCHAR is  -- ARRAY{CHAR} + methods to convert from/to STR
06:        include ARRAY{CHAR};  -- ARRAY{CHAR} の機能拡張クラス ARCHAR を定義
07:     
08:        create(s:STR):SAME is  -- STR から create できるようにする。
09:           res:SAME:=#(s.size);
10:           loop
11:     	 res.set!(s.elt!);
12:           end;
13:           return res;
14:        end;
15:     
16:        to_str:STR is         -- STR に変換できるようにする。
17:           fs:FSTR:=#(self.size);
18:           loop
19:     	 fs:=fs+self.elt!;
20:           end;
21:           return fs.str;
22:        end;
23:     end;  -- ARCHAR
24:     
25:     class XOR < $CRYPT is
26:     
27:        private attr akey:ARCHAR;
28:     
29:        create(key0:STR):SAME is
30:           return new.init(key0);
31:        end;
32:     
33:        private init(key0:STR):SAME is
34:           akey:=#(key0);
35:           return self;
36:        end;
37:     
38:        encrypt(s:STR):STR is
39:           pl:ARCHAR:=#(s);
40:           ci:ARCHAR:=#(s.size);
41:           ENCRYPT::encrypt(#(akey.array_ptr), #(pl.array_ptr), #(ci.array_ptr));  -- C で書かれた関数を呼び出す
42:           return ci.to_str;
43:        end;
44:     
45:        decrypt(s:STR):STR is   -- decrypt is same as the encrypt
46:           return encrypt(s);
47:        end;
48:     end; -- end of XOR
文字列の最後がヌル文字で終わっていることを当てにしている C の関数も問題なく働きます。 C_CHAR_PTR に変換するときヌル文字が付加されるのでしょう。

コンパイルして実行すると次のようになります。

$ sacomp crypt.sa -external ENCRYPT encrypt.c -o crypt
$ ./crypt 'hello world! beautiful beautiful!'
Sipher Test using "hello world! beautiful beautiful!"

Plain send: hello world! beautiful beautiful!
Planin receive: hello world! beautiful beautiful!

XOR send: (表示できない文字列)
XOR receive: hello world! beautiful beautiful!

4. 終わりに

Sather は簡単に C や Fortran と連携することができるので、 ボトルネックに C や Fortran で書かれた実行速度の速い関数を使うことができます。 これによってプログラム全体の実行速度が向上し、 かつ速度に関係しない部分は Sather を使って効率的に書くことができます。 Sather のソースは一度 C に変換されるので、C コンパイラーのプロファイラーを利用して ボトルネックを調べることができます。

付録にこの章で使ったコードをつけておきます。


HOME Sather を試そう download 書き込む