How to build a loadable tcl dll with visual studio (microsoft C compiler)? [crosspost in comp.lang.tcl and comp.lang.c++]

303 views
Skip to first unread message

Michael Reichenbach

unread,
Sep 19, 2007, 6:07:41 PM9/19/07
to
Example one from http://www.tcl.tk/man/tcl8.4/TclCmd/load.htm:

#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.

Don Porter

unread,
Sep 19, 2007, 8:52:50 PM9/19/07
to
Michael Reichenbach wrote:
> static int fooCmd(ClientData clientData,
> Tcl_Interp *interp, int objc, char * CONST objv[]) {

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

Michael Reichenbach

unread,
Sep 19, 2007, 10:07:19 PM9/19/07
to
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?

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.

Eric Hassold

unread,
Sep 20, 2007, 4:51:19 AM9/20/07
to
Hi,

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

Michael Reichenbach

unread,
Sep 20, 2007, 4:43:35 PM9/20/07
to
I wouldn`t say that I have understood everything. But with some magic,
putting some hints together it`s working now. Was really hard for me as
C++ beginner to get this working, because there was nowhere a working
sample on the web. I have written some step by step instructions, if
someone is interested in solving that problem, see here.

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

Reply all
Reply to author
Forward
0 new messages