今回は、Tcl における expr コマンドで利用できる関数を追加した拡張 wish,
bwish を作ります。追加する関数は、サンプルとして立方根を計算する cbrt という C の関数を利用します。もし、お使いの Linux で最新の gcc(正確に言えば、glibc)の開発環境でない場合は念のため
man cbrt でマニュアルが表示されるかどうか確認してください。利用できない場合は、適当に他の利用可能な関数に置き換えてください。MinGW クロスコンパイラの方は C99 に準拠していますので、cbrt を利用できます。
プログラム
プログラムのソースは、
kwish をビルドした時と同じような構成をとります。すなわち
bwish_main.c、
bwish_sub.c および
bwish.h です。
今回のプログラムでは、前回
kwish をビルドするときに利用した API、
Tk_MainLoop を利用せず、代わりに
Tk_Main を使いました。どのやり方が正しい、ということではありませんが、wish あるいは tclsh を拡張して、イベントループを扱うプログラムを作成する際には、
Tk_Main あるいは
Tcl_Main を利用する方法がポピュラーだと思います。
/* bwish_main.c */ #include "bwish.h"
/* type of math value definition */ Tcl_ValueType ArgDouble[] = { TCL_DOUBLE };
/* * Tk_Main acts as the main program for most Tk-based applications. * Starting with Tk 4.0 it is not called main anymore because it is part of * the Tk library and having a function main in a library (particularly * a shared library) causes problems on many systems. Having main in the Tk * library would also make it hard to use Tk in C++ programs, since C++ * programs must have special C++ main functions. * * Normally each application contains a small main function that does nothing * but invoke Tk_Main. Tk_Main then does all the work of creating and running * a wish-like application. */ int main (int argc, char *argv[]) { Tk_Main (argc, argv, AppInit); return EXIT_SUCCESS; }
/* * This procedure provides a “hook” for the application to perform its own * initialization, such as defining application-specific commands. * The procedure must have an interface that matches the type Tcl_AppInitProc: * * typedef int Tcl_AppInitProc(Tcl_Interp *interp); */ int AppInit (Tcl_Interp * interp) { if (Tcl_Init (interp) == TCL_ERROR) return TCL_ERROR;
if (Tk_Init (interp) == TCL_ERROR) return TCL_ERROR;
/* Define application-specific commands and math functions here */ Tcl_CreateMathFunc (interp, "cbrt", 1, ArgDouble, func_cbrt, (ClientData) NULL);
return TCL_OK; }
/* bwish_main.c */ |
コメントに記載してある内容は Tcl/Tk のマニュアルからそのまま写しただけです。要は
main 関数では
Tk_Main を実行してイベントループ状態にするだけで、Tcl/Tk のカスタマイズに関わる処理は
AppInit 関数に記述するようにします。
今回のサンプルで Tcl に追加する立方根の算術関数は、
cbrt という名前で、その処理は、
func_cbrt 関数が担います。この算術関数
cbrt では倍精度実数の引数がひとつ必要なので、Tcl_Value 型で
TCL_DOUBLE を一つ持つ配列をあらかじめ定義しておきます。もし倍精度実数の引数が2つ必要な場合には、例えば次のようにします。
Tcl_ValueType ArgDouble2[] = { TCL_DOUBLE, TCL_DOUBLE };なお、
TCL_DOUBLE の他に
TCL_INT,
TCL_WIDE_INT あるいは
TCL_EITHER を指定できます。
立方根の計算処理部である
func_cbrt 関数は
bwish_sub.c に記述しました。立方根の計算に関わる引数と計算結果は、インタープリタ
interp に対しポインタでやりとりをします。
func_cbrt 関数は int 型で、計算の成否を
TCL_OK (= 0) あるいは
TCL_ERROR (= 1) で返すだけです。
/* bwish_sub.c */ #include "bwish.h"
/***************************************************************************** * int func_cbrt * 拡張数学関数 3√x(立方根)の処理部 * * Tcl 側のコマンド * expr cbrt($n) *****************************************************************************/ int func_cbrt (ClientData clientData, Tcl_Interp * interp, Tcl_Value * args, Tcl_Value * resultPtr) { double x;
x = args->doubleValue;
/* 計算結果 */ resultPtr->type = TCL_DOUBLE; resultPtr->doubleValue = cbrt (x);
return TCL_OK; }
/* bwish_sub.c */ |
インクルードファイル
bwish.h には、このプログラムで使用するインクルードファイルやプロトタイプ関数宣言をまとめました。
/* bwish.h */ #if !defined(BWISH_H) #define BWISH_H
#include <stdio.h> #include <stdlib.h> #include <math.h> #include <tcl.h> #include <tk.h>
/* prototype functions */ extern int AppInit (Tcl_Interp *); extern int func_cbrt (ClientData, Tcl_Interp *, Tcl_Value *, Tcl_Value *);
#endif /* BWISH_H */ /* bwish.h */ |
Windows 用のビルドでは、アイコンをリソースとして用意しました。リソーススクリプト
bwish.rc は、Tcl/Tk のソースファイルにある
win/tclsh.rc などをベースにしています。なおブログの表示の関係で、80文字を越える行は80文字で改行しています。
// bwish.rc // RCS: @(#) $Id: bwish.rc,v 1.2 2008/02/25 10:48:08 bitwalk Exp bitwalk $ // // Version Resource Script //
#include <winver.h> #include <tcl.h>
// // build-up the name suffix that defines the type of build this is. // #if TCL_THREADS #define SUFFIX_THREADS "t" #else #define SUFFIX_THREADS "" #endif
#if STATIC_BUILD #define SUFFIX_STATIC "s" #else #define SUFFIX_STATIC "" #endif
#if DEBUG && !UNCHECKED #define SUFFIX_DEBUG "g" #else #define SUFFIX_DEBUG "" #endif
#define SUFFIX SUFFIX_THREADS SUFFIX_STATIC SUFFIX_DEBUG
LANGUAGE 0x9, 0x1 /* LANG_ENGLISH, SUBLANG_DEFAULT */
VS_VERSION_INFO VERSIONINFO FILEVERSION TCL_MAJOR_VERSION,TCL_MINOR_VERSION,TCL_RELEASE_LEVEL,TCL_RELEASE _SERIAL PRODUCTVERSION TCL_MAJOR_VERSION,TCL_MINOR_VERSION,TCL_RELEASE_LEVEL,TCL_RELEA SE_SERIAL FILEFLAGSMASK 0x3fL #ifdef DEBUG FILEFLAGS VS_FF_DEBUG #else FILEFLAGS 0x0L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Wish Application\0" VALUE "OriginalFilename", "bwish" STRINGIFY(TCL_MAJOR_VERSION) STRI NGIFY(TCL_MINOR_VERSION) SUFFIX ".exe\0" VALUE "CompanyName", "bitWalk Co., Ltd.\0" VALUE "FileVersion", TCL_PATCH_LEVEL VALUE "LegalCopyright", "Copyright \251 2008 by bitWalk Co., Ltd., et al\0" VALUE "ProductName", "bWish " TCL_VERSION " for Windows\0" VALUE "ProductVersion", TCL_PATCH_LEVEL END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END
// // Icon //
bwish ICON DISCARDABLE "bwish.ico"
// bwish.rc |
適当なアイコンファイル
bwish.ico が必要になります。下の関連資料に示した素材サイトなどからダウンロードして適当な ico ファイルを
bwish.ico と名前を変えて使うか、アイコン作成ツールで用意してください。
関連資料で紹介した素材サイトは、利用条件が明記されていたので載せました。他の多くのサイトでもアイコン素材が公開されていますので、興味があれば検索してみてください。
最後に、Linux 用と Windows 用クロスコンパイルのメイクファイルを示します。
# Makefile
CC = gcc RM = rm -f
TARGET = bwish
OBJEXT = o TARGETEXT =
DEFS = INCLUDE = -I/usr/include CFLAGS = -Wall -O2 -std=c99 $(DEFS) $(INCLUDE) LDFLAGS = -L/usr/lib TK_LIBS = -ltk -ltcl LIBS = $(TK_LIBS) -lX11 -lm
OBJS = \ bwish_main.$(OBJEXT) \ bwish_sub.$(OBJEXT)
all: $(TARGET)$(TARGETEXT)
$(TARGET)$(TARGETEXT): $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
.SUFFIXES: .$(OBJEXT) .c.$(OBJEXT): $(CC) -c $(CFLAGS) $< -o $@
$(OBJS): $(TARGET).h clean: $(RM) $(TARGET)$(TARGETEXT) $(OBJS) *~
# Makefile |
MinGW クロスコンパイル環境でビルドするためのメイクファイル
Makefile.win では、windres でリソースをコンパイルしますが、出力の拡張子は
.res.o にしています。
なお、このサンプルでは、拡張した
expr コマンドの算術関数の動作を確認するために、敢えて
-mconsole スイッチをつけてリンクするようにしています。
# Makefile.win
PREFIX = i386-mingw32- CC = $(PREFIX)gcc RC = $(PREFIX)windres RM = rm -f
TARGET = bwish
OBJEXT = o TARGETEXT = .exe RES = res.o TARGET_RES = bwish.$(RES)
DEFS = INCLUDE = -I/usr/local/i386-mingw32/include CFLAGS = -Wall -O2 -std=c99 $(DEFS) $(INCLUDE) LDFLAGS = -L/usr/local/i386-mingw32/lib TK_LIBS = -ltk -ltcl WIN32_LIBS = -lws2_32 -lgdi32 -lcomdlg32 -limm32 \ -lcomctl32 -lshell32 -luuid -lole32 -loleaut32 LDFLAGS_CONSOLE = -mconsole LDFLAGS_WINDOW = -mwindows LIBS = $(TK_LIBS) $(WIN32_LIBS) $(LDFLAGS_CONSOLE)
OBJS = \ bwish_main.$(OBJEXT) \ bwish_sub.$(OBJEXT) \ $(TARGET_RES)
all: $(TARGET)$(TARGETEXT)
$(TARGET)$(TARGETEXT): $(OBJS) $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
.SUFFIXES: .$(OBJEXT) .SUFFIXES: .$(RES) .SUFFIXES: .rc
.c.$(OBJEXT): $(CC) -c $(CFLAGS) $< -o $@
.rc.$(RES): $(RC) -o $@ $<
$(OBJS): $(TARGET).h
clean: $(RM) $(TARGET)$(TARGETEXT) $(OBJS) *~
# Makefile.win |
ビルドと実行
Linux ではビルドして、
bwish を実行すると、
wish を実行したときと同様に、トップレベルウィジェットが表示され、コンソール上は
% のプロンプトが表示され入力待ちになります。
$ make gcc -c -Wall -O2 -std=c99 -I/usr/include bwish_main.c -o bwish_main.o gcc -c -Wall -O2 -std=c99 -I/usr/include bwish_sub.c -o bwish_sub.o gcc -o bwish bwish_main.o bwish_sub.o -L/usr/lib -ltk -ltcl -lX11 -lm $ ./bwish % expr cbrt(27) 3.0 % |
Windows 用のクロスコンパイルでは、ビルド後に Tcl/Tk の
bin ディレクトリへ
bwish.exe をコピーして実行しても、コンソール上にプロンプトが表示されません。仕方がないので、ビルドしたバイナリを Windows へ移して動作確認をしました。コンソールは、コマンドプロンプトのウィンドウが表示されます。
$ make -fMakefile.win clean rm -f bwish.exe bwish_main.o bwish_sub.o bwish.res.o *~ $ make -fMakefile.win i386-mingw32-gcc -c -Wall -O2 -std=c99 -I/usr/local/i386-mingw32/include bwish_main.c -o bwish_main.o i386-mingw32-gcc -c -Wall -O2 -std=c99 -I/usr/local/i386-mingw32/include bwish_sub.c -o bwish_sub.o i386-mingw32-windres -o bwish.res.o bwish.rc i386-mingw32-gcc -o bwish.exe bwish_main.o bwish_sub.o bwish.res.o -L/usr/local/i386-mingw32/lib -ltk -ltcl -lws2_32 -lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32 -mconsole $ cp bwish.exe ../mingw-tcltk-8.5.1-2/bin/ $ cd ../mingw-tcltk-8.5.1-2/bin/ $ ./bwish.exe err:winedevice:ServiceMain driver L"Cdr4_2K" failed to load err:winedevice:ServiceMain driver L"Cdralw2k" failed to load fixme:font:WineEngCreateFontInstance Untranslated charset 255 fixme:comm:set_queue_size insize 4096 outsize 4096 unimplemented stub fixme:imm:ImmReleaseContext (0x10026, 0x158f08): stub |
このところ、立て続けにカスタマイズした wish のビルドの方法について説明をしてきましたが、いかがだったでしょうか。Tcl/Tk を拡張してアプリケーションを構築する際、wish や tclsh を直接拡張する方法の他に、拡張パッケージを作成し、標準の wish や tclsh からロードして拡張機能を利用する方法があります。拡張パッケージの作り方については、過去にホームページで掲載していた資料がありますので、それをベースに書き直して、あらためて
現在のホームページの方に掲載しようと考えています。
関連情報
Tcl/Tk C Library
[1]
Tcl Library[2]
Tk Libraryico ファイル素材&ツール
[1]
[素材]Windows XP ファイル用アイコン[2]
@icon変換 - 画像とアイコンの相互変換ツールC 言語
[1]
プログラミング言語 C の新機能