Reijo Sund
2024-May-28 22:25 UTC
[Rd] How to call directly "dotTcl" C-function of the tcltk-package from the C-code of an external package?
I have a use case with tcltk-package where I need to repeatedly call Tcl/Tk functions very quickly. For such purpose, the standard R-interface turned out to be too slow, and better option has been to call package's C-function dotTcl directly from my own C-code. Before R 4.4.0 it was possible to use getNativeSymbolInfo("dotTcl","tcltk")$address (or R_FindSymbol("dotTcl","tcltk",NULL) in C) to get the function-pointer and then call the function directly, even though it has not been registered as C-callable for other packages. With R 4.4.0 these methods are unable to find the symbol anymore (tested in Linux). I was not able to find what change has caused this new behaviour. Taking a look at tcltk source code, it can be seen that the dotTcl is called using .External within tcltk-package and there is a registration done for it with R_registerRoutines. An object of class NativeSymbolInfo has also been created in the tcltk namespace, and that can be accessed using tcltk:::.C_dotTcl. However, the tcltk:::.C_dotTcl$address is an external pointer of a class RegisteredNativeSymbol and not directly the function pointer to the actual routine. The problem is that there appears not to be any R-level function that would extract the actual function-pointer and that the C-interface for R_RegisteredNativeSymbol has been defined in the internal Rdynpriv.h header that is not included in the public API headers. The only way I was able to access the function directly was using the following C-level approach in which essential parts of the headers are copied from the Rdynpriv.h: #include <R.h> #include <Rinternals.h> #include <R_ext/Rdynload.h> typedef struct { char *name; DL_FUNC fun; int numArgs; R_NativePrimitiveArgType *types; } Rf_DotCSymbol; typedef Rf_DotCSymbol Rf_DotFortranSymbol; typedef struct { char *name; DL_FUNC fun; int numArgs; } Rf_DotCallSymbol; typedef Rf_DotCallSymbol Rf_DotExternalSymbol; struct Rf_RegisteredNativeSymbol { NativeSymbolType type; union { Rf_DotCSymbol *c; Rf_DotCallSymbol *call; Rf_DotFortranSymbol *fortran; Rf_DotExternalSymbol *external; } symbol; }; SEXP(*direct_dotTcl)(SEXP) = NULL; SEXP FindRegFunc(SEXP symbol) { R_RegisteredNativeSymbol *tmp = NULL; tmp = (R_RegisteredNativeSymbol *) R_ExternalPtrAddr(symbol); if (tmp==NULL) return R_NilValue; direct_dotTcl = (SEXP(*)(SEXP)) tmp->symbol.external->fun; return R_NilValue; } Although that works for me, I'm aware that this kind of approach is certainly not recommmended for publicly available external packages. However, I couldn't find any other more legitimate way to access dotTcl function directly from my C-code in R 4.4.0. I have two questions: Would it be possible to get dotTcl C-function (in tcltk.c) of the tcltk-package registered as C-callable from other packages? Was it an intentional change that caused the hiding of the earlier visible (registered) symbols from other packages?
peter dalgaard
2024-May-30 13:54 UTC
[Rd] How to call directly "dotTcl" C-function of the tcltk-package from the C-code of an external package?
I asked Tomas. Apparently this works: getNativeSymbolInfo("dotTcl",PACKAGE=getLoadedDLLs()$tcltk) and the tcltk behavior was changed by r84265 | hornik | 2023-04-15 08:44:36 +0200 (Sat, 15 Apr 2023) | 1 line Try forcing symbols in ff calls. Index: library/tcltk/src/init.c ==================================================================--- library/tcltk/src/init.c (revision 84264) +++ library/tcltk/src/init.c (revision 84265) @@ -66,6 +66,6 @@ { R_registerRoutines(dll, CEntries, NULL, NULL, ExternEntries); R_useDynamicSymbols(dll, FALSE); - R_forceSymbols(dll, FALSE); + R_forceSymbols(dll, TRUE); } I don't know what that was all about, and I'm also a bit puzzled a) that the .External lookup is so slow that you need to bypass it (would have thought that the byte compiler could speed it up) b) that you don't use dotTclObjv and friends to avoid parsing on the Tcl side. - pd> On 29 May 2024, at 00:25 , Reijo Sund <reijo.sund at uef.fi> wrote: > > I have a use case with tcltk-package where I need to repeatedly call Tcl/Tk functions > very quickly. For such purpose, the standard R-interface turned out to be too slow, and > better option has been to call package's C-function dotTcl directly from my own C-code. > > Before R 4.4.0 it was possible to use getNativeSymbolInfo("dotTcl","tcltk")$address (or > R_FindSymbol("dotTcl","tcltk",NULL) in C) to get the function-pointer and then call the > function directly, even though it has not been registered as C-callable for other > packages. > > With R 4.4.0 these methods are unable to find the symbol anymore (tested in Linux). > I was not able to find what change has caused this new behaviour. > > Taking a look at tcltk source code, it can be seen that the dotTcl is called using > .External within tcltk-package and there is a registration done for it with > R_registerRoutines. An object of class NativeSymbolInfo has also been created in the > tcltk namespace, and that can be accessed using tcltk:::.C_dotTcl. > > However, the tcltk:::.C_dotTcl$address is an external pointer of a class > RegisteredNativeSymbol and not directly the function pointer to the actual routine. The > problem is that there appears not to be any R-level function that would extract the actual > function-pointer and that the C-interface for R_RegisteredNativeSymbol has been defined > in the internal Rdynpriv.h header that is not included in the public API headers. > > The only way I was able to access the function directly was using the following C-level > approach in which essential parts of the headers are copied from the Rdynpriv.h: > > #include <R.h> > #include <Rinternals.h> > #include <R_ext/Rdynload.h> > > typedef struct { > char *name; > DL_FUNC fun; > int numArgs; > > R_NativePrimitiveArgType *types; > } Rf_DotCSymbol; > > typedef Rf_DotCSymbol Rf_DotFortranSymbol; > > typedef struct { > char *name; > DL_FUNC fun; > int numArgs; > } Rf_DotCallSymbol; > > typedef Rf_DotCallSymbol Rf_DotExternalSymbol; > > struct Rf_RegisteredNativeSymbol { > NativeSymbolType type; > union { > Rf_DotCSymbol *c; > Rf_DotCallSymbol *call; > Rf_DotFortranSymbol *fortran; > Rf_DotExternalSymbol *external; > } symbol; > }; > > SEXP(*direct_dotTcl)(SEXP) = NULL; > > SEXP FindRegFunc(SEXP symbol) { > R_RegisteredNativeSymbol *tmp = NULL; > tmp = (R_RegisteredNativeSymbol *) R_ExternalPtrAddr(symbol); > if (tmp==NULL) return R_NilValue; > direct_dotTcl = (SEXP(*)(SEXP)) tmp->symbol.external->fun; > return R_NilValue; > } > > > Although that works for me, I'm aware that this kind of approach is certainly not > recommmended for publicly available external packages. However, I couldn't find any > other more legitimate way to access dotTcl function directly from my C-code in R 4.4.0. > > > I have two questions: > > Would it be possible to get dotTcl C-function (in tcltk.c) of the tcltk-package > registered as C-callable from other packages? > > Was it an intentional change that caused the hiding of the earlier visible (registered) > symbols from other packages? > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel-- Peter Dalgaard, Professor, Center for Statistics, Copenhagen Business School Solbjerg Plads 3, 2000 Frederiksberg, Denmark Phone: (+45)38153501 Office: A 4.23 Email: pd.mes at cbs.dk Priv: PDalgd at gmail.com