--- title: "Introduction" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) knitr::knit_engines$set(callme = callme:::callme_engine) library(callme) ``` ```{css, echo=FALSE} .callme { background-color: #E3F2FD; } pre.callme span { background-color: #E3F2FD; } ``` ## 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: 1. Define the C code * in a string (usually called `code` in examples in this package) * in a `.c` file 2. Call `callme::compile(code)` or `callme::compile("file.c")` This standard way of compiling the code in R is shown below: ```{r eval=FALSE} 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). ```{callme} #| invisible=TRUE SEXP print_with_c(SEXP string) { Rprintf("Printing in C: '%s'\n", CHAR(asChar(string))); return R_NilValue; } ``` ```{r} print_with_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`). ```{callme} 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; } ``` ```{r} add(c(1, 2, 3), c(4, 5, 6)) ``` ## 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: ```{callme} #| compile = FALSE, headers = FALSE, rcode = FALSE double *ap = REAL(a); ``` ### Allocte new R objects within C 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. ### Return object from C to R 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()`