Registration of Entry Points in Compiled Code Loaded into R

programming
r
Author

TheCoatlessProfessor

Published

April 28, 2017

Editor’s Note 2018-01-20: This post is largely obsolete as Rcpp 0.12.12 (2017-07-13) and 0.12.13 (2017-09-24) have included autogeneration code to handle the native package registration. For those that are using R’s C interface, the below results still hold.

Intro

With the arrival of R 3.4, a new era of CRAN checks regarding C++ registration was ushered in. In particular, CRAN checks now issue the following “NOTE”:

  • checking compiled code … NOTE

    File ‘packagename/libs/packagename.so’:

    Found no calls to: ‘R_registerRoutines’, ‘R_useDynamicSymbols’

The motivation behind the addition of this check can be found in Writing R Extensions: Section 5.4 Registering native routines and on R-devel: [Rd] Registration of native routines. More aptly, the motivation can be surmised by:

Registering routines has two main advantages: it provides a faster way to find the address of the entry point via tables stored in the DLL at compilation time, and it provides a run-time check that the entry point is called with the right number of arguments and, optionally, the right argument types.

However, the cost of these benefits is apparent to users of Rcpp as a minor inconvenience in the current build cycle. Presently, those that use Rcpp to generate the glue between R and C++ must manually trigger the creation of the package registration. The upside of this generation is it is as painless as merrying R with C++ using Rcpp thanks to R core – more so Kurt Hornik and Prof. Brian Ripley – releasing a tool to automatically create the necessary file. The remainder of this post talks specifically about how to use that tool.

Native routine registration helper

I should begin this section by noting that there is an interaction between devtools and the Windows Rtools Toolchain. So, if you are on Windows, you have followed the below instructions and it still doesn’t work, please note that you are receiving a false positive error.

Consider checking the package against winbuilder run by Uwe Liggs (Official Windows Binary Maintainer CRAN) via devtools::build_win() Alternatively, you can check your package using the new RConsortium funded R-hub Window’s environment via https://github.com/r-hub/rhub.

Prerequisites

The following assumes:

  • R 3.4
  • Package contains compiled code.

Package Registration Steps

To register routines for mypackage, perform the following steps:

  • Set the working directory to the package top-level. e.g. 
setwd("/path/to/mypackage")
/mypackage (<--- this level)
  |- R/
     |- RcppExports.R
     |- ...
  |- src/
     |- RcppExports.cpp
     |- hello_world.cpp
     |- ...
  |- DESCRIPTION
  |- NAMESPACE
  |- ...
  • Automatically generate for 90% of the case the required package generation details using a function in R 3.4’s tools package that is relatively long…
tools::package_native_routine_registration_skeleton(".", "src/init.c")

Within the function call above, we direct the output immediately into the src/init.c file to avoid having to copy and paste from the terminal output. Furthermore, we also use the fact that the working directory is the correct top-level directory within the first argument (e.g. ".").

The src/init.c file for one of my R packages called [sitmo] (with some trunctions) looks 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 sitmo_runif_r(SEXP, SEXP, SEXP);
extern SEXP sitmo_sitmo_two_seeds(SEXP, SEXP);

static const R_CallMethodDef CallEntries[] = {
  {"sitmo_runif_r",            (DL_FUNC) &sitmo_runif_r,            3},
  {"sitmo_sitmo_two_seeds",    (DL_FUNC) &sitmo_sitmo_two_seeds,    2},
  {NULL, NULL, 0}
};

void R_init_sitmo(DllInfo *dll)
{
  R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
  R_useDynamicSymbols(dll, FALSE);
}

Note: The above has been automatically generated similar to the autogeneration magic of RcppExports.R and RcppExports.cpp.

  • Update NAMESPACE by adding the following line:
useDynLib(mypackage, .registration = TRUE)

or if you are using roxygen2 use this roxygen tag:

#' @useDynLib mypackage, .registration = TRUE

As this tag relates to a package level descriptor, I group it with the roxygen2 "_PACKAGE" content block. For example, we have:

#' @useDynLib mypackage, .registration = TRUE
#' @details
#' Juicy package implementation specifics
#' @seealso \code{\link{func}}
"_PACKAGE"
  • (Optional) Update function export preferences in the NAMESPACE file

Furthermore, while in the NAMESPACE file make sure to remove any trace of exportPattern() that is automatically placed there when using package.skeleton(). Commonly, the line looks like:

exportPattern("^[[:alpha:]]+") 

If there is an exportPattern() within the package’s NAMESPACE, this captures a large range of functions. Inadvertently, this now captures RcppExport named functions in C++, e.g. mypackage_function_name. Thus, each function that you are interested in exporting must be explicitly stated in the NAMESPACE file. For example:

export(sample_func)
export(hello_world)

For those using roxygen2, they can use the @export roxygen tag within their code block. Thus, we would have:

#' Some function
#' 
#' Description
#' @param x A \code{integer}.
#' @return Many values
#' Add this export tag below!!!
#' @export
#' @details
#' Sample Function using the roxygen export tag
#' @examples
#' hello_world(1)
hello_world <- function(x) {
  for(i in seq_len(x)) {
    print("Hello World!")
  }
}

Fin

Having said the above piece, the package should now be ready to ship to CRAN in this brave new R 3.4 world. Before shipping, please make sure to test whether your functions still are accessible. If yet to make these tests formal, consider using unit tests via RUnit or testthat.