C言語からTcl言語の解釈機能やTcl APIを使う

 このページから何ページかに渡り、まず最初にサンプルを作ってみてこんなんやなー、 という紹介をしたいと思います。
で、最初の最初にこのページではC言語のプログラムの中からTcl APIを使う例を紹介します。
 で、最初の最初の最初に(だぶってるだぶってる)簡単な例からスタートです。

/* サンプル231(main関数をもつプログラム) */
#include <stdio.h>
#include "tcl.h"

int main(int argc, char* argv[]){
  Tcl_Interp* interp;
  char        bun[256];
  interp = Tcl_CreateInterp();
  strcpy(bun, "expr 3+5");
  Tcl_Eval(interp, bun);
  fprintf(stdout, "%s\n", Tcl_GetStringResult(interp));
  return 0;
}

だいたいナニをするプログラムか予想がつくと思うので、その説明はぬきにして、 さっそくコンパイルしてみましょう。UNIXではだいたいこんな感じになります。

  gcc -o sample1 -I/usr/local/include -L/usr/local/lib \
      sample1.c -ltcl8.4 -lm
-Iと-Lには、Tcl/Tkのヘッダファイル(tcl.h)とライブラリ (libtcl8.4.aまたはlibtcl8.4.soまたは、 それに類するシステムによって決められた名前のファイル) の置かれているディレクトリをそれぞれ指定します。 また「gccがないよ」という方は是非先にgccコンパイラをインストールしましょう。 また、 いくつかの種類のシステムでは -lm のほかにいくつかのスイッチを必要とします。 ポピュラーなシステムについてふれておくと、 サンのSolarisでは「-lnsl -lsocket」、 ヒューレットパッカードのHP-UXでは「-ldld」、 Linuxでは「-ldl」。 シリコングラフィックスのIRIXやFreeBSDでは追加のスイッチは不要です。

一方、MS-WindowsのVisual C++ではだいたいこんな感じになります。

  cl -o sample1.exe -I"C:\Program Files\Tcl\include" \
      sample1.c "C:\Program Files\Tcl\lib\tcl84.lib" \
      "C:\Program Files\Tcl\lib\tk84.lib"
「C:\Program Files\Tcl」の部分は、 Tcl/Tkをインストールするときに指定したインストールディレクトリに適当に置き換えてください。

sample1(sample1.exe) ができたら、 実行すると「8」と出てくるはずです。 ここでは詳しくは触れませんが、この例のように Tcl_Eval というAPIを使えば任意のTcl言語の文をC言語上で実行させることができます。 ではこの「任意のTcl言語の文」をユーザーに対話入力してもらったら? そう、簡単なtclshのできあがりです。

/* サンプル232(main関数をもつプログラム) */
#include <stdio.h>
#include "tcl.h"
#define BUFSIZE 1024

int main(int argc, char* argv[]){
  Tcl_Interp* interp;
  char        bun[BUFSIZE];

  interp = Tcl_CreateInterp();
  Tcl_FindExecutable(argv[0]);
  if(Tcl_Init(interp) != TCL_OK){
    fprintf(stderr, "Tcl initialization error: %s\n",
            Tcl_GetStringResult(interp));
    return 1;
  }
  while(1){
    fprintf(stdout, "input tcl command: ");
    if(! fgets(bun, BUFSIZE, stdin)) break;
    if(Tcl_Eval(interp, bun) == TCL_OK)
      fprintf(stdout, "%s\n", Tcl_GetStringResult(interp));
    else
      fprintf(stdout, "ERROR! %s\n", Tcl_GetStringResult(interp));
  }
  fprintf(stdout, "good bye.\n");
  Tcl_DeleteInterp(interp);
  return 0;
}

Tcl_Initは、 ようするにTcl/Tk処理系をインストールするともれなくついてくるスクリプト、 init.tcl を source しているだけのAPIですが、 このように実行環境に関する各種のグローバル変数をセットしたりするのに使います (この例では、不要なら呼ばなくてもOK)。 で、ここでのポイントは、 API Tcl_EvalはTclコマンドを実行した結果が正常なら TCL_OK、エラーならTCL_ERROR という記号定数を返すことと、 正常に実行された場合の「コマンドの値」、エラーの場合の「エラーメッセージ」をともに Tcl_GetStringResult というAPIでC言語の文字列としてゲットできるということです。

さてそろそろ「そんなことができて、何の意味があるねん」 という方もおられるでしょう。 何にでも価値を求めようとするのは近代資本主義の病理に基づく… などと言い出すつもりはないわけで、 かく言う私うらのもメーカーさんに勤める身としては個人消費が回復して日本が不況を脱出するには産業が価値あるものを生み出すことがこれ重要と考えているわけで、

以下、独り言が続く、と(池)
ひどい〜!(浦)

/* サンプル233 */
#include <stdio.h>
#include "tcl.h"
#define  BUFSIZE 1024
int main(int argc, char* argv[]){
  Tcl_Interp* interp;
  char*       regexp;
  char        buf[BUFSIZE];
  if(argc < 2){
    fprintf(stdout, "usage: %s regexp < textfile\n", argv[0]);
    return 1;
  }
  regexp = argv[1];
  interp = Tcl_CreateInterp();

  while(fgets(buf, BUFSIZE, stdin)){
    switch(Tcl_RegExpMatch(interp, buf, regexp)){
    case -1:  fprintf(stdout, "Regular expression syntax error: %s\n",
              regexp);
              goto eee;
    case 1:   fprintf(stdout, "%s", buf); break;
    default:  break;
    }
  }
eee:return 0;
}

このプログラムはいわゆるgrepです。 この中で使われているTcl_RegExpMatch は本来Tcl言語のregexpコマンドを作るのに使われているAPIで、 これを使えば正規表現のパターン照合という本来複雑な処理の恩恵が簡単に受けられます。 このようにTcl APIはTcl言語を離れて純粋なCのライブラリとして使うこともでき、 とてもベンリです。


GUIつきの腕試し

 さてこんどはいよいよTkのGUI画面を出すプログラムです。

/* サンプル234(main関数をもつプログラム) */
#include <stdio.h>
#include "tcl.h"
#include "tk.h"

int main(int argc, char* argv[]){
  Tcl_Interp* interp;
  char        bun[256];

  interp = Tcl_CreateInterp();

  Tcl_FindExecutable(argv[0]);
  if(Tcl_Init(interp) != TCL_OK){
    fprintf(stderr, "Tcl initialization error: %s\n",
            Tcl_GetStringResult(interp));
    return 1;
  }
  if(Tk_Init(interp) != TCL_OK){
    fprintf(stderr, "Tk initialization error: %s\n",
            Tcl_GetStringResult(interp));
    return 1;
  }

  strcpy(bun, "label  .laba -text {Hello, World} -rel groove -bd 3\n\
               button .cmda -text {Quit} -command exit\n\
               pack .laba .cmda");
  Tcl_Eval(interp, bun);
  Tk_MainLoop();
  return 0;
}

C言語のプログラムの中にモロTcl言語で書かれている部分があるので、 これもナニをするプログラムか説明の必要はないでしょう。 今度はTk_Init、 Tk_MainLoop というTkのAPIが出てきています。 これらの使い方はもうこれが定石ということでこの通りに使い回して結構です。 UNIXでコンパイルするにはこのようにします。

  gcc -o sampletk -I/usr/local/include -I/usr/X11R6/include \
      -L/usr/local/lib -L/usr/X11R6/lib \
      sampletk.c -ltk8.4 -ltcl8.4 -lX11 -lm
今度はXウィンドウシステムのライブラリと、 当然Tkのライブラリもリンクする必要があります。 Tcl/Tkがインストールできて、wishが実行できる以上、 Xウィンドウシステムのライブラリがリンクできないことはありえないので、 よくわからない場合は手近の詳しそうな人に聞いてみてください。

MS-Windowsの場合は最初のsample1.exeのときとほぼ同様です。

  cl -o sampletk.exe -I"C:\Program Files\Tcl\include" \
      sampletk.c "C:\Program Files\Tcl\lib\tcl84.lib" \
      "C:\Program Files\Tcl\lib\tk84.lib"

sampletkができたらさっそく実行。なんとなく感動しましたか? こんなんじゃ飽きたらへんで、 という方は適当に中のTcl言語で書かれているところをいじくってみてくださいね。

なもなも top
(first uploaded 1999/03/12 last updated 2004/05/11, EK - MISUMI URANO)