{callme}
is a package for easily compiling inline C code
for use within R.
Complied C code can be used to improve the speed of critical sections of code e.g. tight loops of numeric operations.
In this introductory vignette, some common elements are described for C code which operates with R objects
The C code chunks in these vignettes is streamlined for display purposes.
In general when using {callme}
you must:
code
in examples in this
package).c
filecallme::compile(code)
or
callme::compile("file.c")
This standard way of compiling the code in R is shown below:
code <- r"(
SEXP print_with_c(SEXP string) {
Rprintf("Printing in C: '%s'\n", CHAR(asChar(string)));
return R_NilValue;
}
)"
callme::compile(code, invisible = TRUE)
print_with_c("hello")
In order to focus on the actual C code (with C code syntax
highlighting), C code will simply be shown in a blue box. Assigning the
code to a string, and calling callme::compile(code)
are
hidden by default (Click to show R code
will reveal this
code).
#include <R.h>
#include <Rinternals.h>
SEXP print_with_c(SEXP string) {
Rprintf("Printing in C: '%s'\n", CHAR(asChar(string)));
return R_NilValue;
}
The following code adds two vectors of floating point values and
returns the result (i.e. a + b
).
#include <R.h>
#include <Rinternals.h>
SEXP add(SEXP a, SEXP b) {
// Sanity checks
if (length(a) != length(b)) {
error("'a' and 'b' must be the same length");
}
// Get a pointer to the actual numeric data in 'a' and 'b'
double *ap = REAL(a);
double *bp = REAL(b);
// Allocate a new R object 'res' and protect it from garbage collection
int N = length(a);
SEXP res = PROTECT(allocVector(REALSXP, N));
// Get a pointer to the actual numeric data in 'res'
double *resp = REAL(res);
// Add elements of two arrays in C
for (int i = 0; i < N; i++) {
resp[i] = ap[i] + bp[i];
}
// Unwind any protection and return the R result
UNPROTECT(1);
return res;
}
code = r"(
#include <R.h>
#include <Rinternals.h>
SEXP add(SEXP a, SEXP b) {
// Sanity checks
if (length(a) != length(b)) {
error("'a' and 'b' must be the same length");
}
// Get a pointer to the actual numeric data in 'a' and 'b'
double *ap = REAL(a);
double *bp = REAL(b);
// Allocate a new R object 'res' and protect it from garbage collection
int N = length(a);
SEXP res = PROTECT(allocVector(REALSXP, N));
// Get a pointer to the actual numeric data in 'res'
double *resp = REAL(res);
// Add elements of two arrays in C
for (int i = 0; i < N; i++) {
resp[i] = ap[i] + bp[i];
}
// Unwind any protection and return the R result
UNPROTECT(1);
return res;
}
)"
callme::compile(code)
The following elements highlighted here are described in more detail in other vignettes within this package.
Function signatures must be of the format
SEXP funcname(SEXP arg1, SEXP arg2, ... SEXP argn)
There is a much greater need for checking for sane arguments in C
compared to R. In R, an out-of-bounds memory access might only result in
an NA
value, but in C such a bad memory access can cause
memory corruption and crashes.
In the example above, the lengths of the two input vectors were checked as automatic vector recyling does not happen in C like it does in R.
All R objects are of type SEXP
and are a combination of
metadata and the actual dta useful to C.
The C compatible data must be extraced from the SEXP
e.g. find the pointer to the array of doubles using:
New R objects can be created within C using
allocVector()
and related functions.
It is important to PROTECT()
any R objects created
within C - otherwise R’s garbage collection will consider them unused
and try to free the memory in which they store data.
The final returned object must also be of type SEXP
.
This object may have been created with a call to
allocVector()
but there are convenience functions for
creating and returning single values
e.g. ScalarInteger()