Tcl拡張の作成

Tclは、TEA(Tcl Extension Architecture)という拡張パッケージを作る際の標準またはガイドラインにより、
C/C++言語記述でコマンドを拡張できます。

拡張パッケージを作成するための、サンプルと手引きは以下のところにあります。

C/C++言語による拡張パッケージの作成方法は、上記ページに譲るとして、
ここでは、拡張パッケージのnamespace化に関して説明をします。

namespace化

まず、tcl.hとtk.hをインクルードする代わりに、tclInt.hとtkInt.hをインクルードします。
tclInt.hとtkInt.hは、Tclのソースコードに含まれています。

#include	"tclInt.h"
#include	"tkInt.h"

Tkを使わない場合は、tkInt.hのインクルードを省略できます。

ここで、パッケージの名前とバージョンを定義します。
パッケージ名とnamespace名は同じでも、違っていても構いません。

#define PACKAGE     "Example"      /* パッケージの名称 */
#define VERSION     "1.0"          /* パッケージの版 */
#define NAMESPACE    "::example"   /* パッケージのNamespace名 */

次に、拡張パッケージの初期化でnamespaceを生成します。

/*
 * 拡張パッケージの初期化
 */
int
Example_Init(
    Tcl_Interp *interp
)
{
    Tcl_Namespace *spacePtr;

    /* Tcl Stubの初期化 */
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
        return TCL_ERROR;
    }

#ifdef USE_TK_STUBS
    /* Tk Stubの初期化 */
    if (Tk_InitStubs(interp, "8.1", 0) == NULL) {
        return TCL_ERROR;
    }
#endif // USE_TK_STUBS

    /* Namespaceの生成 */
    spacePtr = Tcl_CreateNamespace(interp, NAMESPACE, (ClientData)0, (Tcl_NamespaceDeleteProc *) NULL);
    if (spacePtr == NULL) {
        return TCL_ERROR;
    }

    /* コマンドの追加 */
    AddCommand(interp, NAMESPACE, "command1", Example_Command1ObjCmd);

    /* バージョンを返す */
    return Tcl_PkgProvide(interp, PACKAGE, VERSION);
}

AddCommand()は、namespaceにコマンドを追加するためのサブルーチンです。

/*
 * コマンドの追加
 */
static Tcl_Command
AddCommand(
    Tcl_Interp *interp,
    char *nameSpace,
    char *cmdName,
    Tcl_ObjCmdProc *cmdProc
)
{
    char *cmdPath;
    Tcl_DString dString;
    Tcl_Command cmdToken;
    Tcl_Namespace *nsPtr;
    int dontResetList = 0;

    Tcl_DStringInit(&dString);

    /* Namespace::Command を作成 */
    if (nameSpace != NULL) {
        Tcl_DStringAppend(&dString, nameSpace, -1);
    }
    Tcl_DStringAppend(&dString, "::", -1);
    Tcl_DStringAppend(&dString, cmdName, -1);
    cmdPath = Tcl_DStringValue(&dString);

    /* 登録のチェック */
    cmdToken = Tcl_FindCommand(interp, cmdPath, (Tcl_Namespace *)NULL, 0);
    if (cmdToken != NULL) {
        Tcl_DStringFree(&dString);
        return cmdToken;
    }

    /* コマンドの登録 */
    cmdToken = Tcl_CreateObjCommand(interp, cmdPath, cmdProc, NULL, NULL);
    Tcl_DStringFree(&dString);

    /* Namespaceのポインタ取得 */
    nsPtr = Tcl_FindNamespace(interp, nameSpace, (Tcl_Namespace *)NULL, TCL_LEAVE_ERR_MSG);
    if (nsPtr == NULL) {
        return NULL;
    }
    /* コマンドをエクスポート */
    if (Tcl_Export(interp, nsPtr, cmdName, dontResetList) != TCL_OK) {
        return NULL;
    }
    return cmdToken;
}

これでnamespace化は完了です。

パッケージを使ってみる

それでは、Exampleパッケージを使ってみましょう。
package requireコマンドで、Exampleパッケージのバージョン1.0を使うことを宣言すると、
TclがインデックスからExampleパッケージ探してロードします。
すべてのプロシジャをimportすれば、namaspaceのスコープ指定をせずに使えます。

package require Example 1.0
namespace import example::*