순수 C언어로 특정 네임스페이스에 Tcl 커맨드 만들기
이전 강좌에서는 Tcl_Eval을 통하여 특정 네임스페이스에 Tcl 커맨드를 만드는 방법에 대해서 알아보았습니다.
이번 강좌에서는 Tcl_Eval을 사용하지 않고 내부(Internal) Tcl C API를 사용하여 특정 네임스페이스에 Tcl 커맨드를 만드는 방법에 대해서 설명합니다.
특정 네임스페이스에 Tcl 커맨드 만들기
우선, tcl.h와 tk.h를 include 하는 대신, tclInt.h와 tkInt.h를 include 합니다. tclInt.h와 tkInt.h는 Tcl의 소스 코드에 포함되어 있습니다.
#include "tclInt.h"
#include "tkInt.h"
Tk를 사용하지 않는 경우에는 tkInt.h의 include를 생략할 수 있습니다. 여기서 패키지의 이름과 버전을 정의합니다. 패키지명과 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;
}
/* 커맨드 export */
if (Tcl_Export(interp, nsPtr, cmdName, dontResetList) != TCL_OK) {
return NULL;
}
return cmdToken;
}
이로써 namespace화가 되었습니다.
패키지 사용해 보기
이제 Example 패키지를 사용해 봅니다. package require 명령어로 Example 패키지의 버전 1.0을 사용한다고 선언하면, Tcl이 인덱스에서 Example 패키지를 찾아서 로드합니다. 모든 프로시저를 import 하면 namespace의 scope 지정을 하지 않고도 사용할 수 있습니다.
package require Example 1.0
namespace import example::*