{cstructr}
is a demonstration package illustrating how a C struct may
be wrapped and manipulated from within R as an external pointer.
This is a “full-service” implementation which makes the internal values of the struct accessible from R via getters and setters.
For bespoke solutions, a lot of the code could be excised to just keep some core functions.
Note: The code in this package was auto-generated by parsing the C struct definition and the C library function declaration.
typedef struct {
int N;
double strength;
} MyCStruct;
double multiply(MyCStruct *mycstruct);
You can install from GitHub with:
# install.package('remotes')
remotes::install_github('coolbutuseless/cstructr')
library(cstructr)
mycstruct <- MyCStruct(N = 1, strength = 13)
mycstruct
#> MyCStruct(N = 1, strength = 13)
class(mycstruct)
#> [1] "MyCstruct_extptr"
typeof(mycstruct)
#> [1] "externalptr"
mycstruct$N
#> [1] 1
mycstruct$strength
#> [1] 13
mycstruct$N <- 3
mycstruct
#> MyCStruct(N = 3, strength = 13)
rm(mycstruct)
gc()
#> MyCStruct finalizer called to free the C pointer memory
#> used (Mb) gc trigger (Mb) limit (Mb) max used (Mb)
#> Ncells 544954 29.2 1223188 65.4 NA 667645 35.7
#> Vcells 1015419 7.8 8388608 64.0 16384 1824525 14.0
Create multiple structs at once
lots <- MyCStructs(n = 3)
class(lots)
#> [1] "list"
lots
#> [[1]]
#> MyCStruct(N = 0, strength = 0)
#> [[2]]
#> MyCStruct(N = 0, strength = 0)
#> [[3]]
#> MyCStruct(N = 0, strength = 0)
- Write a “bridge function” wrapper (in C) that maps from R objects to C objects
- Call the C library function from this wrapper
- Return the result to R in an appropriate container
An example of a C function in a library is as follows. C libraries know nothing about R or SEXP objects.
double multiply(MyCStruct *mycstruct) {
return mycstruct->N * mycstruct->strength;
}
The “bridge” function between R and the C library call is written in C as follows.
This function unpacks the externalptr to a real MyCStruct
pointer,
calls the C library function, and then returns the results as an R real
numeric value.
SEXP multiply_(SEXP mycstruct_ptr_) {
MyCStruct *mycstruct = external_ptr_to_MyCStruct_ptr(mycstruct_ptr_);
double res = multiply(mycstruct);
return ScalarReal(res);
}
The call from R is:
multiply <- function(mycstruct) {
.Call(multiply_, mycstruct)
}
All these functions combine such that we can create a pointer to a C struct in R, and then call the C library function as follows
library(cstructr)
mycstruct <- MyCStruct(N=2, strength = 4.5)
multiply(mycstruct)
#> [1] 9
A lot of the code to wrap a C function and make it callable from R is standard boilerplate.
For simple library functions, you could auto-generate a lot of this R + C code from the C function declaration.