Making Your .C Less NOTEworthy
If you are a package maintainer, you may have noticed the following new notes from your code checks:
Found no calls to: ‘R_registerRoutines’, ‘R_useDynamicSymbols’
If you are using Rcpp
you can easily fix this by refreshing the auto-generated function registration. However, if you have a lot of C code that uses the .C()
interface, then you need to make a few changes. In this blog post, I'll use my updated meanShiftR package as an example of how to quickly fix your code.
The first thing you need to do is to examine your C code and determine your parameter types for each of the functions you call from R using .C()
. In meanShiftR I have two C functions that I call from R. The first function R_meanShift
has a function prototype that looks like this:
/* mean shift function prototype*/
void R_meanShift(
double * query, /* data to query for, in row major form */
double * train, /* reference data, in row major form */
int * assignment, /* assignment */
int * queryRowPtr, /* number of rows of query data */
int * trainRowPtr, /* number of rows of reference data */
int * queryColPtr, /* number of columns of query and reference*/
int * nNeighborsPtr, /* number of neighbors */
int * iterationsPtr, /* number of iterations */
double * bandwidth, /* bandwidth */
double * alphaPtr, /* ms to newton ajustment parameter */
double * epsilonPtr, /* min l2 dist for convergence */
double * epsilonClusterPtr, /* min l2 dist for clustering */
int * kernelEnumPtr, /* kernel type */
int * algorithmEnumPtr, /* algorithm type */
int * intParameters, /* kernel and alg dependent parameters */
int * dblParameters /* kernel and alg dependent parameters */
);
The second function R_knn
has a function prototype that looks like this:
/* R_knn function prototype */
void R_knn(
double * queryPoints, /* point to query for */
double * x, /* data to reference for the query
(what we build our tree from) */
int * xnrowPtr, /* number of rows of x */
int * nrowPtr, /* number of rows from query points */
int * ncolPtr, /* number of columns for both queryPoints and x */
double * kDist, /* distance vector (we return this) */
int * indexInt, /* index of neighbors (we return this) */
int * kPtr, /* number of nearest neighbors */
double * weight, /* used for weighted distance calculations */
int * leafSizePtr, /* number of nodes in the k-d tree leaf nodes */
double * maxDist /* maximum distance l2^2 to look for neighbors */
);
With this information, I can register these function in four pretty painless steps. (1) Include the R_ext/Rdynload.h in a header file. (2) Create R_NativePrimitiveArgType
s for each C function that you call with .C()
.
(3) Register our C functions as an R_CMethodDef array. (4) Finally, we create a function that is run when we load our function through R; in this function we register our C functions using R_registerRoutines
.
To detail this process, I have created an example C file identifying each of these steps. This isn't necessary, but can simplify things if you call C functions from R that have prototypes defined in separate files. To make things easy to apply to your code, I have included a complete example below, and will then go over each of these steps.
/* init.c */
#include <stdio.h>
#include <stdlib.h>
#include "R.h"
#include "Rinternals.h"
#include "Rmath.h"
/*************************************/
/* STEP: 1. This header file is new */
/*************************************/
#include
#include "kdtree.h"
#include "meanShift.h"
#if defined _OPENMP
#include
#endif
/*************************************/
/* Register SO's */
/*************************************/
/* STEP: 2. Create R_NativePrimitiveArgTypes
for each function */
/*************************************/
static R_NativePrimitiveArgType R_meanShift_t[] = {
REALSXP, REALSXP, INTSXP, INTSXP, INTSXP, INTSXP, INTSXP, INTSXP,
REALSXP, REALSXP, REALSXP, REALSXP, INTSXP, INTSXP
};
static R_NativePrimitiveArgType R_knn_t[] = {
REALSXP, REALSXP, INTSXP, INTSXP, INTSXP, REALSXP, INTSXP, INTSXP,
REALSXP, INTSXP, REALSXP
};
/*************************************/
/* STEP: 3. This is where we register
our C functions as an R_CMethodDef array */
/*************************************/
static const R_CMethodDef cMethods[] = {
{"R_meanShift", (DL_FUNC) &R_meanShift, 14, R_meanShift_t},
{"R_knn", (DL_FUNC) &R_knn, 14, R_knn_t},
{NULL, NULL, 0, NULL}
};
/*************************************/
/* STEP: 4. This is our boilerplate
registration of our C code */
/*************************************/
void R_init_myLib(DllInfo *info)
{
R_registerRoutines(info, cMethods, NULL, NULL, NULL);
R_useDynamicSymbols(info, TRUE);
}
We will start at step two, step one was just including the header file.
Step two involves creating an array of an enumerated types for each C function we call from R, where the enumerated types are from standard R SEXP enumerations:
INTSXP
for integers,
REALSXP
for doubles,
CHARSXP
for characters, etc...
Each of these types are detailed in the R Internals Manual .
Each array is created with type R_NativePrimitiveArgType
and we will be using it in the next step.
In step three we register our functions with the function name, our enumerated array from the last step, and the number of parameters.
We register them through an array of function arrays, where a function array of a function named foo
with enumerated array foo_t
using three parameters would look like:
{"foo", (DL_FUNC) &foo, 3, foo_t}
This array of function arrays is NULL terminated with function array
{NULL, NULL, 0, NULL}
.
The array type we use is R_CMethodDef
and we need to make sure we register all our C functions in this array.
Finally in step four we put it all together in a function that registers our C functions when we load the shared object.
void R_init_myLib(DllInfo *info)
{
R_registerRoutines(info, cMethods, NULL, NULL, NULL);
R_useDynamicSymbols(info, TRUE);
}
That's it!, now update your packages!