TIP:            60
Title:          EXTERN Macro Change to Support a Wider Set of Attributes
Version:        $Revision: 1.21 $
Author:         David Gravereaux <davygrvy@pobox.com>
Author:         Donal K. Fellows <fellowsd@cs.man.ac.uk>
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.

