Introduction
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
Code Layout in Vignettes
The C code chunks in these vignettes is streamlined for display purposes.
In general when using callme you must:
- Define the C code
- in a string (usually called
code
in examples in this package) - in a
.c
file
- in a string (usually called
- Call
callme::compile(code)
orcallme::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;
}
Click to show R code
code = r"(
#include <R.h>
#include <Rinternals.h>
SEXP print_with_c(SEXP string) {
Rprintf("Printing in C: '%s'\n", CHAR(asChar(string)));
return R_NilValue;
}
)"
callme::compile(code)
print_with_c("hello")
#> Printing in C: 'hello'
Example: Add two vectors of floating point numbers
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;
}
Click to show R code
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)
Elements to note in the example
The following elements highlighted here are described in more detail in other vignettes within this package.
Function signature
Function signatures must be of the format
SEXP funcname(SEXP arg1, SEXP arg2, ... SEXP argn)
Sanity checking
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.
Unpack R objects into C equivalents
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: