I'm following this guide to write a simple Tcl extension in C. I have the source code written as he has presented it in the article and I'm compiling with MSVC's command line compiler (cl.exe). My objective is to turn this source code into a shared library (DLL) and allow my Tcl scripts to access this shared library to use the Tcl Extensions I've implemented.
I have Tcl installed in the following directory:
C:\Tcl
Here is the source code:
TclExample.h
#include <tcl.h>
/* <package_name>_Init is called when a package is loaded. In this case,
since our package is named "Random", we call the function Random_Init()
*/
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) int Random_Init(Tcl_Interp* interp);
//EXTERN int Random_Init(Tcl_Interp* interp);
__declspec(dllexport) int RandomCmd(ClientData cd, Tcl_Interp* interp, int argc, char* CONST argv[]);
//EXTERN int RandomCmd(ClientData cd, Tcl_Interp* interp, int argc, char* CONST argv[]);
__declspec(dllexport) int RandomObjCmd(ClientData cd, Tcl_Interp* interp, int objc, Tcl_Obj *CONST objv[]);
//EXTERN int RandomObjCmd(ClientData cd, Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
#ifdef __cplusplus
}
#endif
TclExample.c
#include "TclExample.h"
/* <package_name>_Init is called when a package is loaded. In this case,
since our package is named "Random", we call the function Random_Init()
*/
int Random_Init(Tcl_Interp* interp)
{
/* Initialize the stub table interface */
if (Tcl_InitStubs(interp, "8.1", 0) == NULL){
return TCL_ERROR;
}
/* Create two varations of the "random" procedure.
The orandom command uses the object interface
*/
Tcl_CreateCommand(interp, "random", RandomCmd, NULL, NULL);
Tcl_CreateObjCommand(interp, "orandom", RandomObjCmd, NULL, NULL);
/* Declare that we implement the random package so scripts that do "package require random"
load the library right away
*/
Tcl_PkgProvide(interp, "random", "1.0");
return TCL_OK;
}
/* Defined procedures implementation */
int RandomCmd(ClientData cd, Tcl_Interp* interp, int argc, char* CONST argv[])
{
int rand = 0, error = 0, range = 0;
if (argc > 2){
interp->result = "Incorrect number of args. Usage: random ?range?";
return TCL_ERROR;
}
if (argc == 2){
if (Tcl_GetInt(interp, argv[1], &range) != TCL_OK){
return TCL_ERROR;
}
}
//rand = random();
if (range != 0){
rand %= range;
}
sprintf(interp->result, "%d", rand);
return TCL_OK;
}
int RandomObjCmd(ClientData cd, Tcl_Interp* interp, int objc, Tcl_Obj *CONST objv[])
{
int rand = 0, error = 0, range = 0;
if (objc > 2){
Tcl_WrongNumArgs(interp, 1, objv, "?range?");
return TCL_ERROR;
}
if (objc == 2){
if (Tcl_GetIntFromObj(interp, objv[1], &range) != TCL_OK){
return TCL_ERROR;
}
}
//rand = random();
if (range != 0){
rand %= range;
}
Tcl_Obj* resultPtr = Tcl_GetObjResult(interp);
Tcl_SetIntObj(resultPtr, rand);
return TCL_OK;
}
Here is the command that I'm using from the MSVC Command Line:
cl /I C:\Tcl\include\tcl8.5 /DUSE_TCL_STUBS /DBUILD_tcl /Ox TclExample.c
/link /DLL /LIBPATH C:\Tcl\lib\tclstub85.lib /LIBPATH C:\Tcl\lib\tkstub85.lib
/LIBPATH C:\Tcl\lib\ttkstub.lib /OUT:TclExample.dll
As you can see, I've included the include
paths to the Tcl headers which reside in C:\Tcl\include\tcl8.5
and included the stubs libraries which are located in C:\Tcl\lib
.
I get the following error when I run the above command:
/out:TclExample.exe
/DLL
/LIBPATH
C:\Tcl\lib\tclstub85.lib
/LIBPATH
C:\Tcl\lib\tkstub85.lib
/LIBPATH
C:\Tcl\lib\ttkstub.lib
/OUT:TclExample.dll
TclExample.obj
Creating library TclExample.lib and object TclExample.exp
TclExample.obj : error LNK2019: unresolved external symbol _Tcl_InitStubs referenced in function _Random_Init
TclExample.obj : error LNK2019: unresolved external symbol _tclStubsPtr referenced in function _Random_Init
TclExample.dll : fatal error LNK1120: 2 unresolved externals
I tried doing some digging online but I'm stuck at this point. I looked at the Tcl.h
header and I do see the Tcl_InitStubs function declared there. Also, I'm not sure where the tclStubsPtr error comes from as I'm not using a function by that name in my source code. Does anyone know what the issue here is?
Also, I'm restricted to using the command line for now so I need a command line solution (if possible). I also got my Tcl files from the ActiveTcl distribution.