-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.qmd
181 lines (124 loc) · 4.88 KB
/
README.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
---
format: gfm
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = FALSE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
if (FALSE) {
pkgdown::build_site(override = list(destination = "../coolbutuseless.github.io/package/callme"), new_process = FALSE)
}
library(callme)
```
# callme <img src="man/figures/logo.png" align="right" width="30%" />
<!-- badges: start -->
![](https://img.shields.io/badge/cool-useless-green.svg)
[![CRAN status](https://www.r-pkg.org/badges/version/callme)](https://CRAN.R-project.org/package=callme)
[![R-CMD-check](https://github.com/coolbutuseless/callme/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/coolbutuseless/callme/actions/workflows/R-CMD-check.yaml)
<!-- badges: end -->
`{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` and `PKG_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 C `code` and assign R functions into the nominated `env` in R.
C code could be as a string or in a file.
## Installation
This package can be installed from [CRAN](https://cran.r-project.org/package=callme)
``` r
install.packages('callme')
```
You can install the latest development version from [GitHub](https://github.com/coolbutuseless/callme) with:
``` r
# install.package('remotes')
remotes::install_github('coolbutuseless/callme')
```
Pre-built source/binary versions can also be installed from [R-universe](https://r-universe.dev)
``` r
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.
```{r example_simple, eval = TRUE}
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)
mul(99.5, 0.5)
new_sqrt(c(1, 4, 25, 999))
```
## 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 for `zstd.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.
```{r eval=TRUE}
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()
```
# References
* Hadley's [R internals](https://github.com/hadley/r-internals).
* [Advanced R Book](http://adv-r.had.co.nz/C-interface.html)
has a specfic chapter or R's interface to C.
* Ella Kay's [UserR2024](https://userconf2024.sched.com/event/1c8zS/c-for-r-users-ella-kaye-university-of-warwick) conference presentation: ["C for R users"](https://static.sched.com/hosted_files/userconf2024/84/c-for-r-users.pdf)
* Book: [Deep R Programming](https://deepr.gagolewski.com/chapter/310-compiled.html)
* Davis Vaughan's [Now you C me](https://blog.davisvaughan.com/posts/2019-03-02-now-you-c-me/)
* [c3po](https://github.com/ramiromagno/c3po)
* [R Native API](https://github.com/HenrikBengtsson/RNativeAPI)
### R project official documentation
* Writing R extensions Section 5 [System and foreign language interfaces](https://cran.r-project.org/doc/manuals/R-exts.html#System-and-foreign-language-interfaces)
* [R internals](https://cran.stat.auckland.ac.nz/doc/manuals/r-devel/R-ints.html)