Tcl Source Code

View Ticket
Login
Ticket UUID: 1711975
Title: Tcl_MainEx() (like Tk_MainEx())
Type: RFE Version: None
Submitter: fridolin Created on: 2007-05-03 13:54:41
Subsystem: 50. Embedding Support Assigned To: nijtmans
Priority: 5 Medium Severity:
Status: Closed Last Modified: 2011-11-01 22:53:46
Resolution: Fixed Closed By: nijtmans
    Closed on: 2011-11-01 15:53:46
Description:
This tiny patch adds Tcl_MainEx(). Like Tk_MainEx()
it has an additional argument *interp. This allows
to "extend" interp, before the mainloop starts and the
user cannot write an appInitProc().
Tcl_MainEx() and Tk_MainEx() should be interchangable.
User Comments: nijtmans added on 2011-11-01 22:53:46:

allow_comments - 1

fridolin added on 2010-11-20 03:20:52:
I saw that Tcl_MainExW on Windows is exported, but Tcl_MainEx on Windows isn't exported in HEAD. So I thought it was an oversight.

Thanks for your support!

nijtmans added on 2010-11-19 22:51:45:
Well, having a look at 1711975.patch, the prototype you mention is there!
And when I try it on Linux:
$ nm libtcl8.6.so |grep Tcl_MainEx
00000000000c6850 T Tcl_MainEx

???

However, Tcl8.6b2 release is very near, and
I don't want to check in API-changes now.
After the Tcl8.6b2 release I will discuss this
with other people, and see if there is enough
support to get it in Tcl 8.6

fridolin added on 2010-11-19 18:30:11:

File Added - 393772: tclmainexdecls.patch

fridolin added on 2010-11-19 18:29:12:
I checked out the current HEAD. It works under Windows, but not under Unix (Linux, gcc, -fvisibilty=hidde): the Tcl_MainEx symbol is local (nm says t instead of T). If I added a function declaration to tclDecl.h, it works. I attach the diff.

nijtmans added on 2010-11-07 15:26:57:
Here is a new patch, against HEAD, needed due to
the [FRQ 491789] changes.

nijtmans added on 2010-11-07 15:25:19:

File Added - 392461: 1711975.patch

nijtmans added on 2010-11-07 15:23:12:

File Deleted - 386164:

nijtmans added on 2010-09-10 03:31:09:
Right, new patch attached in which this is fixed.

nijtmans added on 2010-09-10 03:30:39:

File Added - 386164: tclmain.patch

nijtmans added on 2010-09-10 03:29:40:

File Deleted - 381088:

fridolin added on 2010-09-08 21:39:23:
There is a problem with the Patch. Older Compilers don't like the return in

    return Tcl_MainEx(argc, argv, appInitProc, Tcl_CreateInterp());

The function is void.

fridolin added on 2010-07-26 16:49:47:
at least for me, the final patch is fine. I replaced my patched tcl version with HEAD+your patch and everything is working.

nijtmans added on 2010-07-26 04:00:46:
Here is a final patch, which solves some problems in earlier
versions. Two parts of my earlier proposal have been
factored out as separate issues, and are now in HEAD.
Those are:
 [tcl-Bugs-3029891] Functions that don't belong in the stub table
 [tktoolkit-Bugs-3027438] Tk_Main calls Tcl_CreateInterp before Tcl_FindExecutable

With those two separate problems sorted out, the remaining work is much
simpler:
- Introduce Tcl_MainEx, and implement Tcl_Main in terns of Tcl_FindExecutable,
  Tcl_CreateInterp and Tcl_MainEx.
- Introduce a macro Tcl_Main which does the same.

This way, Tcl_Main becomes a macro, 100% equivalent to Tk_Main, while
everything is 100% compatible with before. The shared library still contains
a symbol Tcl_Main, which can be used with GetProcAddress as before.

I don't see any problems with this approach. Does anyone? Ready for
a TIP to be supplied?

nijtmans added on 2010-07-26 03:52:05:

File Added - 381088: tclmain.patch

nijtmans added on 2010-07-26 03:50:09:

File Deleted - 379597:

nijtmans added on 2010-07-09 17:59:37:

File Added - 379597: patch_tk.diff

nijtmans added on 2010-07-09 17:58:57:
Another patch, for Tk, which puts the Tcl_FindExecutable()
call before Tcl_CreateInterp(), by pulling it out of
Tk_MainEx to the Tk_Main macro.

nijtmans added on 2010-07-09 16:38:39:
Uploaded new patch, with restored calling order, as
noted by dkf.

nijtmans added on 2010-07-09 16:37:43:

File Added - 379586: patch.diff

nijtmans added on 2010-07-09 16:37:04:

File Deleted - 379584:

nijtmans added on 2010-07-09 16:30:29:
Commenting on dkf:

>We shouldn't call Tcl_CreateInterp before Tcl_MainEx because there's an
>assumption that Tcl_Main will call Tcl_FindExecutable (and hence
>TclInitSubsystems IIRC) for you, and that's got to be called before
>Tcl_CreateInterp.

I don't think that's true: The Tk_Main macro calls Tcl_CreateInterp before
Tk_MainEx, and wish works!

That said, we could pull the Tcl_FindExecutable call out of Tcl_Main,
and define the macro like:

#define Tcl_Main(argc, argv, proc) \
    (Tcl_FindExecutable(argv[0]),Tcl_MainEx(argc, argv, proc, Tcl_CreateInterpEx()))

That would be a good idea for Tk too, anyway!

nijtmans added on 2010-07-09 16:21:03:
Looks like it might still be controversion, whether this
requires a TIP or not..... So, let's first ask for
opinions. anyone?

========= kennykb citation ==============
Logged In: YES
user_id=99768

Can we please agree to constrain scope - on this particular
patch - to the externals that are exported accidentally? My
opinion is that incompatible change to the export mechanism
for Tcl_Main requires a TIP. The other two hundred symbols
are much less controversial - please let us not throw that
particular baby away with the bathwater.

nijtmans added on 2010-07-09 16:18:59:
This is related to:
<https://sourceforge.net/tracker/?func=detail&atid=310894&aid=1055668&group_id=10894>
===================================================================
Comment By: Joe English (jenglish)
Date: 2004-10-28 20:58

Message:
Logged In: YES
user_id=68433

Currently, to build a version-independent shell that
dynamically loads Tcl, you can use:
 + hTcl = LoadLibrary(...path to tcl8X.dll...)
 + tclMainPtr = GetProcAddress(hTcl, "Tcl_Main")
 + (*tclMainPtr)(argv,argc, yourAppInitProc);

In the scheme you propose, you would instead:
  + hTcl = LoadLibrary( .... )
  + tclCreateInterpPtr = GetProcAddress(hTcl,
"Tcl_CreateInterp");
  + tempInterp = (*tclCreateInterpPtr)(...);
  + Tcl_InitStubs(tempInterp);
  + Tcl_DeleteInterp(tempInterp)
  + Call Tcl_Main() through the stubs table.

This is more complicated, less perspicuous, and isn't even
version-independant since it would only work for versions of
the core where Tcl_Main is in the stubs table.

How is the second one an improvement?  I just don't see it.
========== end of jenglish citation ===========
My remark. After this patch, this would become:
  + hTcl = LoadLibrary( .... )
  + tclCreateInterpPtr = GetProcAddress(hTcl,
"Tcl_CreateInterpEx");
 + tclMainExPtr = GetProcAddress(hTcl,
"Tcl_MainEx");
  + interp = (*tclCreateInterpPtr)(...);
  + tclMainExPtr(...., interp);
Yes, it's a little bit more compilcated than before,
but at least it's the same for Tcl and Tk.

nijtmans added on 2010-07-09 16:08:21:
There is one "potentional incompatibility" here: libtcl.so no longer
exports Tcl_Main and Tcl_CreateInterp but Tcl_MainEx and
Tcl_CreateInterpEx (Tcl_Main and Tcl_CreateInterp are
macros now). The only way someone would notice
this, is when using dlopen+dlsym, as you are doing,
but since this is really unsupported usage I don't mind.

dkf added on 2010-07-09 16:05:37:
We shouldn't call Tcl_CreateInterp before Tcl_MainEx because there's an assumption that Tcl_Main will call Tcl_FindExecutable (and hence TclInitSubsystems IIRC) for you, and that's got to be called before Tcl_CreateInterp. (It'd be a good idea otherwise, but the calling order just isn't there; T_CI needs the filesystem support, and that relies on TIS being run first. Rearranging the startup order is just a recipe for headaches as it is very finicky; what we have now at least has the advantage of working...)

nijtmans added on 2010-07-09 15:45:47:

File Added - 379584: patch.diff

nijtmans added on 2010-07-09 15:44:10:

allow_comments - 0

Re-opening, because maybe there is a way out of this!

The reason I rejected this was because still Tcl_Main and
Tk_Main were still not handled the same. But what if
we introduce a macro Tcl_Main like:
#define Tcl_Main(argc, argv, proc) \
    Tcl_MainEx(argc, argv, proc, Tcl_CreateInterp())
The problem with this is that this would not work
the same when defining TCL_USE_STUBS, because
Tcl_CreateInterp then is supplied through the stub
table which is not initialized yet.

However, there is a solution for this:
- Change Tcl_Main to Tcl_MainEx, adding an extra parameter
  Tcl_Interp
- Change Tcl_CreateInterp to Tcl_CreateInterpEx, which is
  exactly the same.
Those two functions don't need to be documented, because
the Tcl_Main macro is the only supported use of those
(just like Tk_Main). Finally, undefine the Tcl_CreateInterpEx
macro, so this call will never go through the stub table. And
provide a Tcl_CreateInterp macro, to make Tcl_CreateInterp
work exactly as before.

Here is the patch doing this. It does not have the problem
in the original proposal, it really makes Tcl_Main work the
same way as Tk_Main.

nijtmans added on 2010-07-05 15:38:52:

allow_comments - 1

Ok, thanks for your explanation! Is your binding available
somewhere? I would like to have a look at it.

That said, the prefered way of defining an extension is
to have a XXX_Init (say OCaml_Init here), and arrange
for this extension to be loaded with
"package require OCaml". This way, the standard
tclsh and wish can be used. This OCaml_Init
function should do everything you describe,
including calling  back to OCaml to do the
necessary initialization.

If you need non-standard startup arguments,
you can define your own parameters, just
as Tk does. Or, if you want to embed
Tcl in an Ocaml interpreter, I could imagine
some fancy notifier work, but Tcl contains
all machinery to do that. For all of that,
a special Main or appinitproc is not necessary.

The only excuse for defining your own Main
or appinitproc is on a platform which does
not support dynamical loading. Apparently
your platform does, so what's the problem?

Originally, I was under de impression that you
wanted to define a Tcl_MainEx, and a macro
Tcl_Main which was defined exactly like
the Tk_Main equivalent. Tk_MainEx is not
documented, on purpose, because everyone
should use Tk_Main in stead. Granting your
request without defining a macro means
that the new Tcl_MainEx should be
documented, which requires a TIP. And
this TIP promotes a non-recommended
use of Tcl, for which there is an alternative.
So, this TIP has little chance to be accepted
(I would have to vote no to that.... because
of exactly this reason).

Anyway, if your binding code is available
somewhere, I am willing to have a look and
give advise how to do it.

Thanks for your suggestion. Sorry!

fridolin added on 2010-07-05 14:47:08:
Hm. I have a binding to OCaml. I created very early an interp and modify it
on serveral places. It's convenient to do it. I set some variables, I define my own procedures and set a startup script. I spread out this code in several independend modules. Later Tcl_MainEx() is called and everything works as expected.

With the appinitproc approach, I would have to record my modifications
and apply it in the appinitproc. Unfortunatly this is a C-procedure and
it have to call OCaml again. It will work, but it is more work. More badly with
the appinitproc approach I have to distinguish, whether  my call is before of after the main-Loop. Currently I always manipulate a global interp, but without the patch I have to store my modifications for later execution.

BTW, my change is so tiny, it doesn't affect performance or causes code  bloat and I tested it for years now.

Furthermore, I do use the stub table. (But to do it, I load the funtion Tcl_CreateInterp() with dlopen+dlsym.  Or can I call Tcl_InitStubs with interp == NULL? I have to check the documention...)

nijtmans added on 2010-07-02 15:52:03:
Thinking more about it, I was wrong in stating
that Tcl_MainEx should be in the stub table, just
like Tk_MainEx. There are 4 functions in Tk which
are only meant to be called from tkAppInit.c,
never from stub-enabled extensions. Those are:
Tk_MainEx, Tk_Init, Tk_SafeInit and
Tk_CreateConsoleWindow. If they are
used in a stub-enabled extension, the
author surely misunderstands those
function. So, it would be better
to remove those 4 from the stub
table, or - at least disable them -
so extension authors cannot make
this mistake.

For Tcl, the same holds for Tcl_Main
and Tcl_FindExecutable, the latter
should never have been put in the
stub table!

This means, that it makes your idea
of making Tcl_MainEx and Tk_MainEx
function the same less usefull. For Tk,
Tk_Main is a macro, should Tcl_Main
made a macro as well? You want
to extend interp before the mainloop
starts, but the appinitproc is already
called before the main loop starts.
So, what's the added value? I'm
not sure any more.

fridolin added on 2010-07-01 19:44:00:
Where can I attach here a file? Here is an update for HEAD:

? MainEx-HEAD.patch
Index: doc/Tcl_Main.3
===================================================================
RCS file: /cvsroot/tcl/tcl/doc/Tcl_Main.3,v
retrieving revision 1.20
diff -r1.20 Tcl_Main.3
8c8
< '\" 
---
> '\"
10c10
< '\" 
---
> '\"
21a22,23
> \fBTcl_MainEx\fR(\fIargc, argv, appInitProc, interp\fR)
> .sp
36a39,40
> .AP Tcl_Interp *interp in
> The main interpreter
56c60
< to developers of shell applications, so they do not have to 
---
> to developers of shell applications, so they do not have to
90a95,100
> \fBTcl_MainEx\fR takes as an additional parameter the main interpreter. So
> the caller may create variables or commands, before \fBTcl_Main\fR runs.
> \fBTcl_MainEx\fR and \fBTk_MainEx\fR are not interchangable, because
> \fBTk_MainEx\fR runs an event loop and \fBTcl_MainEx\fR does not.
> \fBTcl_MainEx\fR is also present in the stub library.
> .PP
110c120
< by \fIencodingPtr\fR, when that is not NULL.  
---
> by \fIencodingPtr\fR, when that is not NULL.
140c150
< commands.  The application initialization routine might also 
---
> commands.  The application initialization routine might also
Index: generic/tcl.decls
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tcl.decls,v
retrieving revision 1.175
diff -r1.175 tcl.decls
2321a2322,2330
> 
> # Feature Request 1711975
> 
> declare 630 generic {
>     void Tcl_MainEx (int argc, char **argv,
>          Tcl_AppInitProc *appInitProc, Tcl_Interp * interp)
> }
> 
> 
Index: generic/tcl.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tcl.h,v
retrieving revision 1.307
diff -r1.307 tcl.h
2371a2372,2374
> EXTERN void Tcl_MainEx (int argc, char **argv,
>                         Tcl_AppInitProc *appInitProc, Tcl_Interp * interp);
> 
Index: generic/tclDecls.h
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclDecls.h,v
retrieving revision 1.177
diff -r1.177 tclDecls.h
3705a3706,3712
> #ifndef Tcl_MainEx_TCL_DECLARED
> #define Tcl_MainEx_TCL_DECLARED
> /* 630 */
> EXTERN voidTcl_MainEx(int argc, char **argv,
> Tcl_AppInitProc *appInitProc,
> Tcl_Interp *interp);
> #endif
4370a4378
>     void (*tcl_MainEx) (int argc, char **argv, Tcl_AppInitProc *appInitProc, Tcl_Interp *interp); /* 630 */
6920a6929,6932
> #ifndef Tcl_MainEx
> #define Tcl_MainEx \
> (tclStubsPtr->tcl_MainEx) /* 630 */
> #endif
Index: generic/tclMain.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclMain.c,v
retrieving revision 1.50
diff -r1.50 tclMain.c
226c226
<  * Tcl_Main --
---
>  * Tcl_MainEx --
228a229
>  *      It gets the main interp as an argument.
243c244
< Tcl_Main(
---
> Tcl_MainEx(
246c247,248
<     Tcl_AppInitProc *appInitProc)
---
>     Tcl_AppInitProc *appInitProc,
>     Tcl_Interp * interp)
257d258
<     Tcl_Interp *interp;
262,263d262
<     interp = Tcl_CreateInterp();
<     Tcl_InitMemory(interp);
608a608,644
> /*----------------------------------------------------------------------
>  *
>  * Tcl_Main --
>  *
>  *Main program for tclsh and most other Tcl-based applications.
>  *
>  * Results:
>  *None. This function never returns (it exits the process when it's
>  *done).
>  *
>  * Side effects:
>  *This function initializes the Tcl world and then starts interpreting
>  *commands; almost anything could happen, depending on the script being
>  *interpreted.
>  *
>  *----------------------------------------------------------------------
>  */
> 
> void
> Tcl_Main(
>     int argc,/* Number of arguments. */
>     char **argv,/* Array of argument strings. */
>     Tcl_AppInitProc *appInitProc)
> /* Application-specific initialization
>  * function to call after most initialization
>  * but before starting to execute commands. */
> {
> 
>     Tcl_Interp * interp = Tcl_CreateInterp();
> #ifdef TCL_MEM_DEBUG
>     Tcl_InitMemory(interp);
> #endif
>     Tcl_MainEx(argc, argv, appInitProc, interp);
> 
> }
> 
> 
Index: generic/tclStubInit.c
===================================================================
RCS file: /cvsroot/tcl/tcl/generic/tclStubInit.c,v
retrieving revision 1.191
diff -r1.191 tclStubInit.c
1126a1127
>     Tcl_MainEx, /* 630 */

fridolin added on 2010-03-19 20:21:33:
I'll updated the patch for Tcl 8.6. 

 - Tcl_InitMemory() in a ifdef
 - The Tcl_Main man page has notes about Tcl_MainEx. (I don't find
    any documentation for Tk_MainEx).
 - Tcl_MainEx is now in the stub table

fridolin added on 2010-03-19 20:18:08:

File Added - 367386: MainEx-HEAD.patch

nijtmans added on 2008-11-07 16:48:26:
Looks like a good idea. However, I have three comments:
- The Tcl_InitMemory() should be done in Tcl_MainEx as
  follows:
    #ifdef TCL_MEM_DEBUG
        Tcl_InitMemory(interp);
    #endif
  just as is done in Tk_MainEx as well.
- Tcl_MainEx() and Tk_MainEx() are not interchangable, because
  Tk_MainEx() runs an event loop and Tcl_MainEx() does not.
  That doesn't matter, only it should not be documented
  as such. Documentation is missing from the patch.
- Tcl_MainEx() belongs in the stub table, just as Tk_MainEx.

Further on, I agree with this patch.

fridolin added on 2008-11-06 20:20:06:

File Added - 300397: MainEx-8.5.5.patch

File Added: MainEx-8.5.5.patch

dgp added on 2007-05-03 21:36:36:

data_type - 360894

fridolin added on 2007-05-03 20:54:41:

File Added - 227682: MainEx.patch

Attachments: