Welcome to the first actual post in the R4 series, following the short announcement earlier this week.
Last month, Brian Ripley announced on r-devel that registration of routines would now be tested for by R CMD check
in r-devel (which by next month will become R 3.4.0). A NOTE will be issued now, this will presumably turn into a WARNING at some point. Writing R Extensions has an updated introduction) of the topic.
Package registration has long been available, and applies to all native (i.e. "compiled") function via the .C()
, .Call()
, .Fortran()
or .External()
interfaces. If you use any of those -- and .Call()
may be the only truly relevant one here -- then is of interest to you.
Brian Ripley and Kurt Hornik also added a new helper function: tools::package_native_routine_registration_skeleton()
. It parses the R code of your package and collects all native function entrypoints in order to autogenerate the registration. It is available in R-devel now, will be in R 3.4.0 and makes adding such registration truly trivial.
But as of today, it requires that you have R-devel. Once R 3.4.0 is out, you can call the helper directly.
As for R-devel, there have always been at least two ways to use it: build it locally (which we may cover in another R4 installment), or using Docker. Here will focus on the latter by relying on the Rocker project by Carl and myself.
We assume you can run Docker on your system. How to add it on Windows, macOS or Linux is beyond our scope here today, but also covered extensively elsewhere. So we assume you can execute docker
and e.g. bring in the 'daily r-devel' image drd from our Rocker project via
~$ docker pull rocker/drd
With that, we can use R-devel to create the registration file very easily in a single call (which is a long command-line we have broken up with one line-break for the display below).
The following is real-life example when I needed to add registration to the RcppTOML package for this week's update:
~/git/rcpptoml(master)$ docker run --rm -ti -v $(pwd):/mnt rocker/drd \ ## line break
RD --slave -e 'tools::package_native_routine_registration_skeleton("/mnt")'
#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 RcppTOML_tomlparseImpl(SEXP, SEXP, SEXP);
static const R_CallMethodDef CallEntries[] = {
{"RcppTOML_tomlparseImpl", (DL_FUNC) &RcppTOML_tomlparseImpl, 3},
{NULL, NULL, 0}
};
void R_init_RcppTOML(DllInfo *dll)
{
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
}
edd@max:~/git/rcpptoml(master)$
We can understand the docker
command invocation above through its components:
docker run
is the basic call to a container--rm -ti
does subsequent cleanup (--rm
) and gives a terminal (-t
) that is interactive (-i
)-v $(pwd):/mnt
uses the -v a:b
options to make local directory a
available as b
in the container; here $(pwd)
calls print working directory to get the local directory which is now mapped to /mnt
in the containerrocker/drd
invokes the 'drd' container of the Rocker projectRD
is a shorthand to the R-devel binary inside the container, and the main reason we use this container-e 'tools::package_native_routine_registration_skeleton("/mnt")
calls the helper function of R (currently in R-devel only, so we use RD) to compute a possible init.c
file based on the current directory -- which is /mnt
inside the containerThat it. We get a call to the R function executed inside the Docker container, examining the package in the working directory and creating a registration file for it which is display to the console.
src/init.c
We simply copy the output to a file src/init.c; I often fold one opening brace one line up.
We also change one line in NAMESPACE from (for this package) useDynLib("RcppTOML")
to useDynLib("RcppTOML", .registration=TRUE)
. Adjust accordingly for other package names.
And with that we a have a package which no longer provokes the NOTE as seen by the checks page. Calls to native routines are now safer (less of a chance for name clashing), get called quicker as we skip the symbol search (see the WRE discussion) and best of all this applies to all native routines whether written by hand or written via a generator such as Rcpp Attributes.
So give this a try to get your package up-to-date.
This post by Dirk Eddelbuettel originated on his Thinking inside the box blog. Please report excessive re-aggregation in third-party for-profit settings.