With the next major version of R (3.4.0), registering compiled functions in packages will become mandatory. This is an excellent step - to vaguely demystify it, at the moment when you call a C/C++/FORTRAN function from R, there’s a good chance R has to search every compiled object in your session trying to find it. Registration is akin to implicitly adding a namespace (::
) directive; it means R knows precisely where to look. Much faster!
The problem, at least for me, is that the registration is expected for externally-exposed .c
functions, which in Rcpp packages are automatically generated and slightly alien, and the R Extensions manual makes registration sound incredibly intimidating.
After much swearing I’ve just finished registering routines for wicket, and figured I’d write up a short guide on doing it with Rcpp. This should become entirely useless advice when R 3.4.0 is out.
Generating registration code
The core of registration is a registration routine for each exposed function. This provides a little reference for each function, and confirms how many arguments they take and what the arguments’ types are (usually SEXPs). tools
actually has a function, package_native_routine_registration_skeleton
, for generating these routines automatically! Except it’s in the development branch and so your options are:
- Installing a dev version of R and hoping it doesn’t mess your system up;
- Navigating to the development source code, in SVN or the github mirror, copying the functions, copying a couple of other functions in totally different files which are needed but not exposed, and..argh.
I’ve made this a bit easier by copying the functions out, adding references to the non-exposed functions they depend on, and sticking them in a Gist, so you just have to run:
devtools::source_gist("b93b589b17c3315f766d3e2988533558", filename = "routines.R")
Once you’ve got this, run:
package_native_routine_registration_skeleton(dir = "path/to/the/base/directory/of/your/package")
This should print a load of C to your terminal that looks a bit like:
#include <R.h>
#include <Rinternals.h>
#include <stdlib.h> // for NULL
#include <R_ext/Rdynload.h>
/* FIXME:
Check these declarations against the C/Fortran source code.
*/
/* .Call calls */
extern SEXP test_func(SEXP);
static const R_CallMethodDef CallEntries[] = {
{"test_func", (DL_FUNC) &test_func, 1},
{NULL, NULL, 0}
};
void R_init_diffr(DllInfo *dll)
{
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
}
Copy it into a file called init.c
, in the src
directory of your package. From my use of it the generation code is pretty reliable, but (as the comment suggests) you’re still encouraged to test it. Compare it to RcppExports.R
and check:
- Is there an
extern
entry for every function inRcppExports.R
? - Does the number at the end of each line in the
R_CallMethodDef
entry match the number of arguments to each function inRcppExports.R
?
If yes and yes, you’re most likely fine. If not, tweak init.c
until they line up. Either way, be sure to run all the standard checks just to be on the safe side.
Specifying registration
The second step is making sure your package knows the functions are registered, and should use registration; this can be achieved by modifying the useDynLib
entry in your NAMESPACE
file.
If you’ve been manually generating NAMESPACE
, simply change useDynLib(packagename)
to useDynLib(packagename, .registration = TRUE)
. If you’ve been using roxygen, go to whichever file that contains your @useDynLib packagename
statement and change it to @useDynLib wicket, .registration = TRUE
. Regenerate the documentation and clean and rebuild, and that pesky function registration complaint on win-builder should go away entirely.
And that’s it! Much easier than it looked on the surface. When R 3.4.0 comes out and package_native_routine_registration_skeleton
becomes more easily accessible, this’ll get a lot less finnicky, and hopefully a lot of your Rcpp packages should be registered by the time that happens and you won’t have to worry about it at all unless you alter the externally-facing code.