CritclはTclのソースにCなどのコードを埋め込んで、"on the fly"にコンパイルする拡張です。PerlでいうところのinlineCです。速くなると思います。ベンチマークも取ってみたいと思います。
いまのとこ(critcl0.34)Windows+mingwではgccのコマンドパスの作成がちょっとおかしくてそのままだとコンパイルできないので、改造します。ていうか、空白パスをエスケープしたりするだけなんですが。単純にWindowsだとホームディレクトリへのパスに空白があるので、
C:/Documents and Settings/MyAccount/.critcl //FAIL C:/Documents\ and\ Settings/MyAccount/.critcl //OK
と適当に引っ掛かるところだけエスケープするだけです。面倒なので差分だけ置いておきます。
4a5,8 > proc escape {str} { > return [string map {{ } {\ }} $str] > } > 587c591 < lappend copts -I$cache --- > lappend copts -I[escape $cache] 600,601c604 < < set cmdline "$v::compile $copts -o $libfile $base.c $srcs" --- > set cmdline "$v::compile $copts -o [escape $libfile] [escape $base.c] $srcs"
他にも直すところがあるかもしれません。
package require critcl #整数を返す critcl::cproc add {int a int b} int { return a + b; } #実数を返す critcl::cproc addf {double a double b} double { return a + b; } #文字列を返す(長さは不変) critcl::cproc shiftc {char* c int len} char* { int i; for (i=0; i<len; i++) { c[i] = c[i]+1; } return c; } #Tclオブジェクト critcl::cproc newtclobj {} Tcl_Obj* { Tcl_Obj *obj = Tcl_NewStringObj("Hello World?", -1); Tcl_IncrRefCount(obj); return obj; } #Tclオブジェクト(リスト) critcl::cproc list_push {Tcl_Interp* interp Tcl_Obj* list Tcl_Obj* obj} ok { if (Tcl_IsShared(list)) list = Tcl_DuplicateObj(list); Tcl_ListObjAppendElement(interp, list, obj); Tcl_SetObjResult(interp, list); return TCL_OK; }
最後のやつみたいにリストや配列なんかの場合はSWIG使った方が楽な場面もあると思う。
package require critcl critcl::ccode { #include <stdio.h> #include <stdlib.h> } critcl::cproc testput {} void { char *str = malloc(sizeof(char)*100); int i; for (i=0; i<100; i++) sprintf(str+i, "%d", i%10); for (i=0; i<10; i++) printf("%c\n", str[i]); free(str); return; }
counter.h
#ifndef _DEF_H_COUNTER #define _DEF_H_COUNTER extern int incrCount(); extern int CountVal; #endif
counter.c
#include "counter.h" int CountVal = 0; int incrCount() { return ++CountVal; }
test.tcl
package require critcl critcl::cheaders counter.h critcl::csources counter.c critcl::ccode { #include <stdio.h> #include <stdlib.h> #include "counter.h" } critcl::cproc countUp {} void { int i; for (i=0; i<10; i++) { incrCount(); printf("%d\n", CountVal); } return; }
あと、ライブラリをリンクするときは
critcl::clibraries file
みたいな。
critclではライブラリを作ることもできます。
test.tcl
package require critcl critcl::cproc add {int a int b} int { return a + b; /* this is C code */ } critcl::cproc addf {double a double b} double { return a + b; /* this is C code */ }
これをライブラリにするには、
tclkit critcl.kit -lib test.tcl
とすると、test.dllが作成されます。これを使うには普通にtclshで
load test.dll
などとしてやればいいです。Win,Mac,UNIXでMakefileを書き分けたりせずに簡単にコンパイルできるのが楽でいいと思いましたです。
パッケージにすることもできます。
cmath.tcl
package require critcl package provide cmath 1.0 namespace eval cmath { critcl::cproc add {int a int b} int { return a + b; /* this is C code */ } critcl::cproc addf {double a double b} double { return a + b; /* this is C code */ } }
tclkit critcl.kit -pkg test.tcl
libディレクトリ以下にパッケージを作ります。
lib以下にはターゲットマシンごとのライブラリが存在します。が、特定のプラットフォーム決め撃ちの時はpkgIndex.tclに次のように書いておけばpkgIndex.tclとライブラリの二つのファイルだけでいけます。
package ifneeded cmath 1.0 " package provide cmath 1.0; [list load [file join $dir cmath.dll] cmath]; "
C++を使う場合は次のようにする。
package require critcl critcl::config language c++ critcl::clibraries -lstdc++
critcl::config I # -Iオプション critcl::config L # -Lオプション critcl::config appinit # ? critcl::config combine # ? critcl::config force # 強制的に再コンパイルする critcl::config keepsrc # ソースファイルを出力 critcl::config language # 言語指定(-x) critcl::config lines # ? critcl::config outdir # 出力ディレクトリ? critcl::config tk # ?
source critcl.kit package require critcl proc tcl_tak {x y z} { if {$x <= $y} {return $z} return [tcl_tak \ [tcl_tak [expr $x-1] $y $z] \ [tcl_tak [expr $y-1] $z $x] \ [tcl_tak [expr $z-1] $x $y] \ ] } critcl::ccode { static int tak (int x, int y, int z) { if (x<=y) return z; return tak( tak(x-1, y, z), tak(y-1, z, x), tak(z-1, x, y) ); } } critcl::cproc c_tak {int x int y int z} int { return tak(x,y,z); } set x 20 set y 10 set z 5 puts "Tcl [time {tcl_tak $x $y $z} 10]" puts "C [time {c_tak $x $y $z} 10]"
一回目
Tcl 1614874 microseconds per iteration C 93094 microseconds per iteration
二回目
Tcl 1612537 microseconds per iteration C 3317 microseconds per iteration
注:一回目はCのコードをコンパイルするので遅いです。