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使った方が楽な場面もあると思う。

Cのヘッダファイルなどを埋め込み

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;
}

Cのファイルとリンク

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++も使えるみたい

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のコードをコンパイルするので遅いです。

リンク

コメントをどーぞ



CategoryTclTk


|New|Edit|Freeze|Diff|History|Attach|Copy|Rename|
Last-modified: 2005-05-03 (Tue) 00:00:00
HTML convert time: 0.010 sec.