Making Your .C Less NOTEworthy

[This article was first published on MeanMean, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

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_knnhas 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_NativePrimitiveArgTypes 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!

To leave a comment for the author, please follow the link and comment on their blog: MeanMean.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)