simplec - demo package for calling C code with .C()

July 4, 2020 · View on GitHub

simplec is a small demo package showing how C code could be included in a package and called with .C()

In general: Consider using .Call() or Rcpp instead of .C()

This is one of a series of small demo packages for
calling other languages from R:

Rough comparison of .C(), .Call(), {Rcpp} (and .Fortran())

.C().Call()Rcpp
OverviewNo real understanding of R objectsNeed to understand SEXP macros & internalsC++ classes hide the complexity of the SEXP internals
What code?Basic C code. Numeric calcs.Complex C code. Can manipulate R objects from CComplex C and C++ code involving numerics and R objects
ProsSimple to understand and useSimple. No unnecessary copying.Great documentation. Wrapping of R objects very intuitive.
ConsToo simple for most interesting thingsNeed to understand SEXP & R internals
ConsPerforms copying of data to call functions
Demo R package{simplec}{simplecall}{simplercpp}
Compiled size17 kB17 kB92 kB (stripping can bring this down: see issue1)
.Fortran()
OverviewNo real understanding of R objects
What code?Basic Fortran code. Numeric calcs.
ProsSimple to understand and use
ConsToo simple for most interesting things
ConsPerforms copying of data to call functions
ConsNeed to know Fortran!
Demo R package{simplefortran}
Compiled size17 kB
                         |

Installation

You can install from GitHub with:

# install.package('remotes')
remotes::install_github('coolbutuseless/simplec)

What’s in the box?

Package contains 2 C functions, and 2 functions in R which call the C functions using .C().

C functionR function
add_(double *x, double *y, double *res)add_C(x, y)
mul_(double *x, double *y, double *res)mul_C(x, y)

What’s in the R functions?

  • A call using .C()
  • First argument is the C function name e.g. add_
  • x and y arguments are passed through from the R function
  • the third argument to the function is space in which to store the result i.e. numeric(1)
  • .C() returns as many arguments as you gave it originally
  • We only want to return the actual result, which is the third argument, hence [[3]]
  • @useDynLib [packagename] [C function name] is used to generate the NAMESPACE entry useDynLib(simplec,add_)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#' Add two numbers
#'
#' @param x,y numbers to add
#'
#' @useDynLib simplec add_
#' @export
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
add_C <- function(x, y) {
  .C(add_, x, y, numeric(1))[[3]]
}

What’s in the C functions?

  • C function returns void
  • all arguments passed as pointers
  • Actual result must be stored in one of the arguments
void add_(double* x, double* y, double* res) {
  res[0] = x[0] + y[0];
}

How do R variables map to C variables?

RC
logicalint *
integerint *
doubledouble *
complexRcomplex *
characterchar **
rawunsigned char *

What does the C function look like in R?

  • An object of class NativeSymbolInfo
  • Holds an externalptr to the loaded function
simplec:::add_
#> $name
#> [1] "add_"
#> 
#> $address
#> <pointer: 0x7f9780cb1c30>
#> attr(,"class")
#> [1] "RegisteredNativeSymbol"
#> 
#> $dll
#> DLL name: simplec
#> Filename:
#>         /Library/Frameworks/R.framework/Versions/4.0/Resources/library/simplec/libs/simplec.so
#> Dynamic lookup: FALSE
#> 
#> $numParameters
#> [1] 3
#> 
#> attr(,"class")
#> [1] "CRoutine"         "NativeSymbolInfo"

Resources

Acknowledgements

  • R Core for developing and maintaining such a wonderful language.
  • CRAN maintainers, for patiently shepherding packages onto CRAN and maintaining the repository