callme
callme copied to clipboard
Easily compile inline C code for R
callme 
{callme}
compiles inline C code and generates wrappers so that the C
code can be easily called from R.
Features:
- Compile inline C code (or code from a file) and makes it immediately (and easily!) available to R.
- Accepts complete C code - including function declaration and header
#include
directives. - Explicit handling for
CFLAGS
,PKG_CPPFLAGS
andPKG_LIBS
for setting compiler flags, C pre-processor flags, and library linking flags, respectively. - Generates R functions to call the compiled C functions.
- Multiple function definitions allowed in a single code block.
What’s in the box
-
compile(code, CFLAGS, PKG_CPPFLAGS, PKG_LIBS, env, verbosity)
compile the Ccode
and assign R functions into the nominatedenv
in R. C code could be as a string or in a file. -
cflags_default()
the default C compiler flags R uses on your system
Installation
This package can be installed from CRAN
install.packages('callme')
You can install the latest development version from GitHub with:
# install.package('remotes')
remotes::install_github('coolbutuseless/callme')
Pre-built source/binary versions can also be installed from R-universe
install.packages('callme', repos = c('https://coolbutuseless.r-universe.dev', 'https://cloud.r-project.org'))
Example
The following example compiles a code snippet into a C library and creates a wrapper function in R (of the same name) which can be used to call the compiled code.
library(callme)
code <- "
#include <R.h>
#include <Rinternals.h>
// Add 2 numbers
SEXP add(SEXP val1, SEXP val2) {
return ScalarReal(asReal(val1) + asReal(val2));
}
// Multiply 2 numbers
SEXP mul(SEXP val1, SEXP val2) {
return ScalarReal(asReal(val1) * asReal(val2));
}
// sqrt elements in a vector
SEXP new_sqrt(SEXP vec) {
SEXP res = PROTECT(allocVector(REALSXP, length(vec)));
double *res_ptr = REAL(res);
double *vec_ptr = REAL(vec);
for (int i = 0; i < length(vec); i++) {
res_ptr[i] = sqrt(vec_ptr[i]);
}
UNPROTECT(1);
return res;
}
"
# compile the code
compile(code)
# Call the functions
add(99.5, 0.5)
#> [1] 100
mul(99.5, 0.5)
#> [1] 49.75
new_sqrt(c(1, 4, 25, 999))
#> [1] 1.00000 2.00000 5.00000 31.60696
Linking against an installed library
In this example we want to get the version of the zstd
library (which
has already been installed on the computer), and return it as a
character string.
We need to tell R when compiling the code:
- to look in the
/opt/homebrew/include
directory forzstd.h
. - to look for the actual
zstd
library in/opt/homebrew/lib
. - to link to the
zstd
library (-lzstd
)
Note: This code works for zstd
installed via homebrew
on macOS.
Paths will be different for other operating systems.
code <- r"(
#include <R.h>
#include <Rinternals.h>
#include "zstd.h"
SEXP zstd_version() {
return mkString(ZSTD_versionString());
}
)"
# Compile the code
compile(code,
PKG_CPPFLAGS = "-I/opt/homebrew/include",
PKG_LIBS = "-L/opt/homebrew/lib -lzstd")
# Call the function
zstd_version()
#> [1] "1.5.6"
References
- Hadley’s R internals.
- Advanced R Book has a specfic chapter or R’s interface to C.
- Ella Kay’s UserR2024 conference presentation: “C for R users”
- Book: Deep R Programming
- Davis Vaughan’s Now you C me
- c3po
- R Native API
R project official documentation
- Writing R extensions Section 5 System and foreign language interfaces
- R internals