#include <tcl.h>
#include <stdio.h>
static int fooCmd(ClientData clientData,
Tcl_Interp *interp, int objc, char * CONST objv[]) {
printf("called with %d arguments\n", objc);
return TCL_OK;
}
int Foo_Init(Tcl_Interp *interp) {
if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
return TCL_ERROR;
}
printf("creating foo command");
Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);
return TCL_OK;
}
My compile.bat (using the current MinGW gcc port):
set djgpp=c:\MinGW\djgpp.env
set path=%path%;c:\MinGW\bin
gcc -I C:\Tcl\include -s -shared -o a.dll a.cpp C:\Tcl\bin\tcl84.dll
pause
First example doesn`t compile. There is an error.
gcc -I C:\Tcl\include -s -shared -o
a.dll a.cpp C:\Tcl\bin\tcl84.dll
a.cpp: In function `int Foo_Init(Tcl_Interp*)':
a.cpp:13: error: invalid conversion from `int (*)(void*, Tcl_Interp*,
int, char*
const*)' to `int (*)(void*, Tcl_Interp*, int, Tcl_Obj* const*)'
a.cpp:13: error: initializing argument 3 of `Tcl_Command_*
Tcl_CreateObjComman
d(Tcl_Interp*, const char*, int (*)(void*, Tcl_Interp*, int, Tcl_Obj*
const*), v
oid*, void (*)(void*))'
Example two:
#include <windows.h>
#include <tcl.h>
#ifndef DECLSPEC_EXPORT
#define DECLSPEC_EXPORT __declspec(dllexport)
#endif // DECLSPEC_EXPORT
BOOL APIENTRY
DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return TRUE;
}
EXTERN_C int DECLSPEC_EXPORT
Tcldemo_Init(Tcl_Interp* interp)
{
#ifdef USE_TCL_STUBS
Tcl_InitStubs(interp, "8.3", 0);
#endif
Tcl_Obj *version = Tcl_SetVar2Ex(interp, "tcldemo_version", NULL,
Tcl_NewDoubleObj(0.1), TCL_LEAVE_ERR_MSG);
if (version == NULL)
return TCL_ERROR;
int r = Tcl_PkgProvide(interp, "Tcldemo", Tcl_GetString(version));
// Call Tcl_CreateObjCommand etc.
return r;
}
EXTERN_C int DECLSPEC_EXPORT
Tcldemo_SafeInit(Tcl_Interp* interp)
{
// We don't need to be specially safe so...
return Tcldemo_Init(interp);
}
This compiled with same compile.bat without any errors or warnings.
I wanted to compile the second example also with visual studio
(microsoft C compiler) and I did add the c:\tcl\include and the
c:\tcl\lib paths. But it doesn`t work, there is an errormessage:
Error 1 error C2664: 'Tcl_CreateObjCommand' : cannot convert parameter 3
from 'int (__cdecl *)(ClientData,Tcl_Interp *,int,char *const [])' to
'Tcl_ObjCmdProc (__cdecl *)'
How can I fix it?
You also don`t need to answer this specific question. Any other example
code which will compile in visual studio and which can be used later as
a loadable tcl module would be much appreciated.
With that prototype, your fooCmd routine is a
Tcl_CmdProc. It's a command procedure that takes
string arguments...
> Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);
...but you are passing fooCmd to
Tcl_CreateObjCommand() and Tcl_CreateObjCommand
expects a Tcl_ObjCmdProc, and not a Tcl_CmdProc.
The compiler is telling you this error.
Either convert your fooCmd routine to be a
Tcl_ObjCmdProc -- a command procedure that
takes (Tcl_Obj *) arguments -- or call
Tcl_CreateCommand() instead of
Tcl_CreateObjCommand().
DGP
I think this problem could compiler based.
Unfortunately what you wrote did not help me.
If you are using in visual studio 7 (2005) or 8 (ocras 2008) yourself
and get such an extension running, please post the code here.
Michael Reichenbach wrote :
> Well, I got the example from here:
> http://www.tcl.tk/man/tcl8.4/TclCmd/load.htm#M8. 8.4 is the current
> stable tcl release and this is the official documentation. Did them use
> a used wrong commands inside the official documentation example for years?
Definately, documentation is buggy, objv argument should have
declaration "Tcl_Obj *CONST objv[]", not char*. Error is still there in
8.4.16-rc1 doc. This may deserve a quick bug report and fix before
8.4.16 gets out.
>
> I think this problem could compiler based.
>
> Unfortunately what you wrote did not help me.
You only need to fix fooCmd declaration:
static int fooCmd(ClientData clientData,
Tcl_Interp *interp, int objc, Tcl_Obj * CONST objv[]) {
printf("called with %d arguments\n", objc);
return TCL_OK;
}
and it will compile fine with mingw, cygwin, vs2k5 or any other
compiler. I suggests you also define USE_TCL_STUBS=1
(-DUSE_TCL_STUBS=1), and link with tclstub84.lib instead of tcl84.dll.
This will make your extension more portable across versions and build
environments.
>
> If you are using in visual studio 7 (2005) or 8 (ocras 2008) yourself
> and get such an extension running, please post the code here.
Eric
1. File -> new project -> name: Foo -> win32 console application -> dll
2. Tools -> Options -> VC++ Directorys -> Include Files -> added
C:\Tcl\include
3. C++ -> precompiled headers -> Not Using Precompiled Headers
4. Library files -> C:\Tcl\lib
5. Project settings -> Configuration Properties -> Linker -> Input ->
Additional Dependencies -> added c:\tcl\lib\tclstub84.lib
6. use this source (my mistake was to use the #define after #include)
(without the extern "C" it`s also not working, you will get error
message while % load Foo.dll, it says can`t find Foo_Init procedure)
#define USE_TCL_STUBS 1
#include <tcl.h>
#include <stdio.h>
extern "C"
{
__declspec(dllexport) int Foo_Init(Tcl_Interp* interp);
}
static int fooCmd(ClientData clientData, Tcl_Interp *interp, int objc,
Tcl_Obj * CONST objv[]) {
printf("called with %d arguments\n", objc);
return TCL_OK;
}
int Foo_Init(Tcl_Interp *interp) {
if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
return TCL_ERROR;
}
printf("creating foo command");
Tcl_CreateObjCommand(interp, "foo", fooCmd, NULL, NULL);
return TCL_OK;
}
7. Debug -> change to Release
8. Build -> Build solution -> compiles without any errors -> in project
folder under release is File Foo.dll with ~6 kb