FortranからTcl/Tkを呼び出す。
Fortran初心者の高木です。おはようございます。
まだ始めたばかりのFortranはわからないことだらけです。
何をやるにも手探りの状態です。
うまくいったと思っても、実際には間違っていたり、もっとよい方法があったりすると思います。
初心者は大体こんなもんなのでしょうけど。
Fortranを勉強していて難しいと感じるのは、やはり処理系による差異です。
ネット検索して得られる情報も、特定の処理系に依存していることが多いので、何が処理系依存で何がそうでないのかの見極めに手間取ります。
やろうとすることによっては処理系に依存せざるを得ないこともあります。
今回挑戦しようとしているFortranからTcl/Tkを呼び出すのもそのひとつです。
FortranからTcl/Tkを呼び出すといっても、パイプで標準入出力をつないで別プロセスとして呼び出すといった方法ではなく、Cのライブラリ(すなわちTcl API)を使ってTcl/Tkの機能を利用することにします。
最初から凝ったことはできませんので、内容は簡単なものにしたいと思います。
「exit」と書かれたボタンを1つだけ貼り付けて、それをクリックすれば終了するというごくシンプルなものです。
純粋なTcl/Tkで書けば1行で済んでしまいます。
1 | pack [button .b -text exit -command exit] |
次に、これをCを使って実現してみることにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <tk.h> int main(int argc, char *argv[]) { Tcl_Interp *interp = Tcl_CreateInterp(); Tcl_FindExecutable(argv[0]); Tcl_Init(interp); Tk_Init(interp); Tcl_Eval(interp, "pack [button .b -text exit -command exit]"); Tk_MainLoop(); Tcl_Exit(0); } |
1行とはいいませんが、まだ大したことはありません。
ところが、Fortranでこれをやろうとすると途端に難度が上がります。
まず、Fortranから直接CのTcl APIを呼ぶことはできませんので、ラッパーを用意する必要がありません。
ラッパーはCで書きますが、これがどうしても処理系に依存してしまいます。
今回はgfortranを使うので、関数名の末尾に下線を付けることでFortranからCの関数を呼び出せるようになります。
また、Fortranでは、関数やプロシージャの引数は必ず参照渡しになりますので、Cではポインタとして受け取ることになります。
それ以外のコーリングコンベンションはとくに意識しなくてもよさそうです。
それを踏まえて書いてみました。
まずはCで書いたラッパーからです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #include <stdlib.h> #include <tk.h> static Tcl_Interp *interp; int tk_init_(const char *argv0) { int r = -1; interp = Tcl_CreateInterp(); Tcl_FindExecutable(argv0); if (Tcl_Init(interp) != TCL_OK) goto failure; if (Tk_Init(interp) != TCL_OK) goto failure; r = 0; failure: return r; } void tk_main_loop_() { Tk_MainLoop(); Tcl_Finalize(); } int tk_eval_(char *script, int *n) { return Tcl_EvalObjEx(interp, Tcl_NewStringObj(script, *n), 0); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ! main.f08 program main character script*100 character result*256 character arg0*256 call getarg(0, arg0) if (tk_init(arg0) < 0) then stop end if script = "pack [button .b -text exit -command exit]" if (tk_eval(script, len_trim(script)) /= 0) then stop end if call tk_main_loop(); end program main |
結構大変でした。
Fortran側は本来であればimplicit none
を使うべきなのですが、Cで定義した関数やプロシージャがIMPLICITだと怒られてコンパイルできません。
何かうまい回避策があるのかもしれませんが、私にはわかりませんでした。
本格的にラッパーを作るのであれば、Cで書いたラッパーを呼び出すFortranのモジュールを作って、アプリケーションからはそのモジュールを利用するのが現実的なのかもしれません。