TIP: 60 Title: EXTERN Macro Change to Support a Wider Set of Attributes Version: $Revision: 1.21 $ Author: David Gravereaux Author: Donal K. Fellows State: Rejected Type: Project Vote: Done Created: 06-Sep-2001 Post-History: Tcl-Version: 8.6 ~ Abstract This TIP proposes a change to how the EXTERN macro in ''tcl.h'' works to support a wider range of compiler specific attributes. ~ Rationale 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. ftp://tcl.activestate.com/pub/tcl/misc/example.zip 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 |#else |# define EXPORT(a,b) a b |#endif | |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. Is> | EXTERN int Foobar_Init (Tcl_Interp *interp); Proposed> | TCL_EXTERN(int) Foobar_Init (Tcl_Interp *interp); ~ Reference Implementation https://sourceforge.net/tracker/download.php?group_id=10894&atid=310894&file_id=70480&aid=436116 ~ Examples Is: |EXTERN int |Foobar_Init (Tcl_Interp *interp) |{ |#ifdef USE_TCL_STUBS | if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { | return TCL_ERROR; | } |#endif | Tcl_CreateObjCommand(interp, "foobar", FooBar, NULL, NULL); | return TCL_OK; |}; Proposed: |TCL_EXTERN(int) |Foobar_Init (Tcl_Interp *interp) |{ |#ifdef USE_TCL_STUBS | if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { | return TCL_ERROR; | } |#endif | Tcl_CreateObjCommand(interp, "foobar", FooBar, NULL, NULL); | return TCL_OK; |}; Preprocessor output is the following: > Borland: |/* 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: */}; > VC++: |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. ~ Copyright This document has been placed in the public domain.