大阪市中央区 システムソフトウェア開発会社

営業時間:平日09:15〜18:15
MENU

FortranからTcl/Tkを呼び出す。

著者:高木信尚
公開日:2019/01/14
最終更新日:2019/01/14
カテゴリー:技術情報
タグ:

Fortran初心者の高木です。おはようございます。

まだ始めたばかりのFortranはわからないことだらけです。
何をやるにも手探りの状態です。
うまくいったと思っても、実際には間違っていたり、もっとよい方法があったりすると思います。
初心者は大体こんなもんなのでしょうけど。

Fortranを勉強していて難しいと感じるのは、やはり処理系による差異です。
ネット検索して得られる情報も、特定の処理系に依存していることが多いので、何が処理系依存で何がそうでないのかの見極めに手間取ります。

やろうとすることによっては処理系に依存せざるを得ないこともあります。
今回挑戦しようとしているFortranからTcl/Tkを呼び出すのもそのひとつです。

FortranからTcl/Tkを呼び出すといっても、パイプで標準入出力をつないで別プロセスとして呼び出すといった方法ではなく、Cのライブラリ(すなわちTcl API)を使ってTcl/Tkの機能を利用することにします。
最初から凝ったことはできませんので、内容は簡単なものにしたいと思います。
「exit」と書かれたボタンを1つだけ貼り付けて、それをクリックすれば終了するというごくシンプルなものです。
純粋なTcl/Tkで書けば1行で済んでしまいます。

pack [button .b -text exit -command exit]

次に、これをCを使って実現してみることにします。

#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で書いたラッパーからです。

#include 
#include 

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);
}
! 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のモジュールを作って、アプリケーションからはそのモジュールを利用するのが現実的なのかもしれません。

    前の記事 :
    上に戻る