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:
- tclmainexdecls.patch [download] added by fridolin on 2010-11-19 18:30:10. [details]
- 1711975.patch [download] added by nijtmans on 2010-11-07 15:25:18. [details]
- MainEx-HEAD.patch [download] added by fridolin on 2010-03-19 20:18:08. [details]
- MainEx-8.5.5.patch [download] added by fridolin on 2008-11-06 20:20:06. [details]
- MainEx.patch [download] added by fridolin on 2007-05-03 20:54:41. [details]