TIP 60: EXTERN Macro Change to Support a Wider Set of Attributes

EuroTcl/OpenACS 11 - 12 JULY 2024, VIENNA
Author:         David Gravereaux <[email protected]>
Author:         Donal K. Fellows <[email protected]>
State:          Rejected
Type:           Project
Vote:           Done
Created:        06-Sep-2001
Tcl-Version:    8.6


This TIP proposes a change to how the EXTERN macro in tcl.h works to support a wider range of compiler specific attributes.


With working on Borland support recently, I found that luckily the newest "free commandline tools" http://www.borland.com/bcppbuilder/freecompiler/ does support Microsoft's __declspec(dllexport) attribute. But at the same time, the older way with __export is still valid, but can't be used due to the order within the prototype declaration of the EXTERN macro.

What's this with the MS compiler:

	__declspec(dllexport) __cdecl int func (int a, int b);

will have to be this with Borland:

	int __export __cdecl func (int a, int b);

The order of the attribute needs to be after the return type.

Even though __declspec is supported in the Microsoft style with version 5.5+ of the Borland compiler, if EXTERN could swap around the order a hair, old Turbo C v5.0 has a better chance to make a DOS library. Should someone feel the need.

Let's leave the existing EXTERN macro as-is and just make a new one called TCL_EXTERN to support the new behavior.

Karl Lembuaer (sp?) did a presentation @ OSCON regarding his recent tinytcl project %TODO: add link here% about his DOS port of Tcl 6.7 for use in a hand-held device.

Stepping backward for DOS support, may actually be a leap forward in an off-beat manner...

Rejected Alternatives

I saw something like this in a very old DDE extension that someone at Sun wrote. It was used as an example windows extension for years.


In example.h is this:

#if defined(__WIN32__)
#   if defined(_MSC_VER)
#	define EXPORT(a,b) __declspec(dllexport) a b
#   else
#	if defined(__BORLANDC__)
#	    define EXPORT(a,b) a _export b
#	else
#	    define EXPORT(a,b) a b
#	endif
#   endif
#   define EXPORT(a,b) a b

EXTERN EXPORT(int,Example_Init) _ANSI_ARGS_((Tcl_Interp *interp));

That work is doing the same job, but I prefer the method that I'm proposing.

It is also mentioned on http://tcl.activestate.com/doc/howto/winext.html and feel it is rather out-of-date and the order issue with __export should be brought into the core with this patch and be fix for good.


	EXTERN int Foobar_Init (Tcl_Interp *interp);


	TCL_EXTERN(int) Foobar_Init (Tcl_Interp *interp);

Reference Implementation




Foobar_Init (Tcl_Interp *interp)
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
        return TCL_ERROR;
    Tcl_CreateObjCommand(interp, "foobar", FooBar, NULL, NULL);
    return TCL_OK;


Foobar_Init (Tcl_Interp *interp)
    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
        return TCL_ERROR;
    Tcl_CreateObjCommand(interp, "foobar", FooBar, NULL, NULL);
    return TCL_OK;

Preprocessor output is the following:


/* foobar.c 14: */extern  int __export
/* foobar.c 15: */Foobar_Init (Tcl_Interp *interp)
/* foobar.c 16: */{
/* foobar.c 17: */
/* foobar.c 18: */if (Tcl_InitStubs(interp, "8.1", 0) == 0) {
/* foobar.c 19: */return 1;
/* foobar.c 20: */}
/* foobar.c 21: */
/* foobar.c 22: */(tclStubsPtr->tcl_CreateObjCommand)(interp, "foobar", FooBar, 0, 0);
/* foobar.c 23: */return 0;
/* foobar.c 24: */};


extern  __declspec(dllexport) int
Foobar_Init (Tcl_Interp *interp)

    if (Tcl_InitStubs(interp, "8.1", 0) == ((void *)0)) {
        return 1;
#line 22 "foobar.c"
    (tclStubsPtr->tcl_CreateObjCommand)(interp, "foobar", FooBar, ((void *)0), ((void *)0));
    return 0;

MinGW (native gcc on win):

extern       int
Foobar_Init (Tcl_Interp *interp)

    if (Tcl_InitStubs(interp, "8.1", 0) == ((void *)0) ) {
        return 1 ;

    (tclStubsPtr->tcl_CreateObjCommand) (interp, "foobar", FooBar, ((void *)0) , ((void *)0) );
    return 0 ;

Random Notes

In tclInt.h starting around line 1916, are prototypes for the internal cmdprocs. I can't think of any reason why they should be exported. Also note the comment about line:1673, as it states:

 * Procedures shared among Tcl modules but not used by the outside
 * world:

As the current EXTERN macro places ""everything"" exportable, the use of EXTERN following this comment in tclInt.h is contradictory. In place of EXTERN for this purpose I used the new TCL_EXTRNC in the reference implementation.


This document has been placed in the public domain.