[Rd] How to call directly "dotTcl" C-function of the tcltk-package from the C-code of an external package?

Reijo Sund re|jo@@und @end|ng |rom ue|@||
Wed May 29 00:25:54 CEST 2024


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?



More information about the R-devel mailing list