package_native_routine_registration_skeleton {tools} | R Documentation |
Write Skeleton for Adding Native Routine Registration to a Package
Description
Write a skeleton for adding native routine registration to a package.
Usage
package_native_routine_registration_skeleton(dir, con = stdout(),
align = TRUE, character_only = TRUE, include_declarations = TRUE)
Arguments
dir |
Top-level directory of a package. |
con |
Connection on which to write the skeleton: can be specified as a file path. |
align |
Logical: should the registration tables be lined up in three columns each? |
character_only |
Logical: should only |
include_declarations |
Logical: should the output include declarations (also known as ‘prototypes’) for the registered routines? |
Details
Registration is described in section ‘Registering native routines’ of ‘Writing R Extensions’. This function produces a skeleton of the C code which needs to be added to enable registration, conventionally as file ‘src/init.c’ or appended to the sole C file of the package.
This function examines the code in the ‘R’ directory of the
package for calls to .C
, .Fortran
, .Call
and
.External
and creates registration information for those it can
make sense of. If the number of arguments used cannot be determined
it will be recorded as -1
: such values should be corrected.
Optionally the skeleton will include declarations for the registered
routines: they should be checked against the C/Fortran source code,
not least as the number of arguments is taken from the R code. For
.Call
and .External
calls they will often suffice, but
for .C
and .Fortran
calls the ‘void *’ arguments
would ideally be replaced by the actual types. Otherwise declarations
need to be included (they may exist earlier in that file if appending
to a file, or in a header file which can be included in
‘init.c’).
The default value of character_only
is appropriate when working
on a package without any existing registration:
character_only = FALSE
can be used to suggest updates for a package which has been
extended since registration. For the default value, if .NAME
values are found which are not character strings (e.g. names
or expressions) this is noted via a comment in the output.
Packages which used the earlier form of creating R objects for native
symbols via additional arguments in a useDynLib
directive will probably most easily be updated to use registration
with character_only = FALSE
.
If an entry point is used with different numbers of arguments in the
package's R code, an entry in the table (and optionally, a
declaration) is made for each number, and a comment placed in the
output. This needs to be resolved: only .External
calls can
have a variable number of arguments, which should be declared as
-1
.
A surprising number of CRAN packages had calls in R code to native routines not included in the package, which will lead to a ‘loading failed’ error during package installation when the registration C code is added.
Calls which do not name a routine such as .Call(...)
will be
silently ignored.
Value
None: the output is written to the connection con
.
Extracting C/C++ prototypes
There are several tools available to extract function declarations from C or C++ code.
For C code one can use cproto
(https://invisible-island.net/cproto/cproto.html; Windows
executables are available), for
example
cproto -I/path/to/R/include -e *.c
ctags
(commonly distributed with the OS)
covers C and C++., using something like
ctags -x *.c
to list all function usages. (The ‘Exuberant’ version allows a lot more control.)
Extracting Fortran prototypes
gfortran
9.2 and later can extract C prototypes for Fortran
subroutines with a special flag:
gfortran -c -fc-prototypes-external file.f
although ironically not for functions declared bind(C)
.
Note
This only examines the ‘R’ directory: it will not find
e.g. .Call
calls used directly in examples, tests etc.
Static code analysis is used to find the .C
etc calls: it
will find those in parts of the R code ‘commented out’
by inclusion in if(FALSE) { ... }
.
On the other hand, it will fail to find the entry points in
constructs like
.Call(if(int) "rle_i" else "rle_d", i, force)
and does not know the value of variables in calls like
.Call (cfunction, ...) .Call(..., PACKAGE="sparseLTSEigen")
(but if character_only
is false, will extract the first as
"cfunction"
). Calls which have not been fully resolved will be
noted via comments in the output file.
Call to entry points in other packages will be ignored if they have an
explicit (character string) PACKAGE
argument.
See Also
Examples
## Not run:
## with a completed splines/DESCRIPTION file,
tools::package_native_routine_registration_skeleton('splines',,,FALSE)
## produces
#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 spline_basis(SEXP, SEXP, SEXP, SEXP);
extern SEXP spline_value(SEXP, SEXP, SEXP, SEXP, SEXP);
static const R_CallMethodDef CallEntries[] = {
{"spline_basis", (DL_FUNC) &spline_basis, 4},
{"spline_value", (DL_FUNC) &spline_value, 5},
{NULL, NULL, 0}
};
void R_init_splines(DllInfo *dll)
{
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
R_useDynamicSymbols(dll, FALSE);
}
## End(Not run)