-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathREADME.Rmd
340 lines (253 loc) · 10.7 KB
/
README.Rmd
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "man/figures/README-",
out.width = "100%"
)
library(simplecall)
```
# simplecall - demo package for calling C code with `.Call()`
<!-- badges: start -->
![](https://img.shields.io/badge/cool-useless-green.svg)
[![R build status](https://github.com/coolbutuseless/simplecall/workflows/R-CMD-check/badge.svg)](https://github.com/coolbutuseless/simplecall/actions)
<!-- badges: end -->
`simplecall` is a small demo package showing how C code could be included
in a package and called with `.Call()`.
This package now includes demo code for:
* creating a list in C
* creating a data.frame in C
You can install from [GitHub](https://github.com/coolbutuseless/simplecall) with:
``` r
# install.package('remotes')
remotes::install_github('coolbutuseless/simplecall)
```
## What's in the box?
Package contains example C functions and R wrappers to call them
| C function | R function |
|-------------------------------------------|--------------|
| `SEXP add_(SEXP x_, SEXP y_)` | `add_Call(x, y)` |
| `SEXP create_list_in_c_()` | `create_list_in_c()` |
| `SEXP create_data.frame_in_c_()` | `create_data_frame_in_c()` |
## What's in the R functions?
* A call using `.Call()`
* First argument is the C function name e.g. `add_`
* Other arguments to the R function can be passed through to C
```{r eval = FALSE}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#' Add two numbers
#'
#' @param x,y numbers to add
#'
#' @useDynLib simplecall add_
#' @export
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
add_Call <- function(x, y) {
.Call(add_, x, y)
}
```
## What's in the C functions?
* C function returns an `SEXP` pointer which is a real R object
* all arguments passed as SEXP pointers
```
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Add 2 numbers
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP add_(SEXP x_, SEXP y_) {
int xtype = TYPEOF(x_);
int ytype = TYPEOF(y_);
if (xtype != REALSXP | ytype != REALSXP) {
Rf_error("add_() expects only floating point numbers i.e. doubles or reals");
}
int xlen = length(x_);
int ylen = length(y_);
if (xlen != 1 | ylen != 1) {
error("add_() expects arguments to be length = 1");
}
SEXP result = PROTECT(allocVector(REALSXP, 1));
REAL(result)[0] = asReal(x_) + asReal(y_);
UNPROTECT(1);
return result;
}
```
## Creating an R List within C
Example code is included to create a list completely within C and return it to R
<details>
<summary> Click to show/hide code </summary>
```{c eval = FALSE}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creating a list within C and returning it to R
//
// 1. Create individual integer/real/whatever vectors
// 2. allocate a VECSXP of the correct size
// 3. assign each member into the list
// 4. create names and assign them to the list
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP create_list_in_c_() {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Each member of the list gets allocated separately
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int n = 10;
SEXP idx_ = PROTECT(allocVector(INTSXP , n));
SEXP x_ = PROTECT(allocVector(REALSXP, n));
SEXP y_ = PROTECT(allocVector(REALSXP, n));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Assign some dummy values into the members
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for (int i = 0; i < n; i++) {
INTEGER(idx_)[i] = i + 1;
REAL(x_)[i] = i + 10;
REAL(y_)[i] = i + 100;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Allocate a list with 3 members
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP list_ = PROTECT(allocVector(VECSXP, 3));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Add members to the list
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SET_VECTOR_ELT(list_, 0, idx_);
SET_VECTOR_ELT(list_, 1, x_);
SET_VECTOR_ELT(list_, 2, y_);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Set the names on the list.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP names = PROTECT(allocVector(STRSXP, 3));
SET_STRING_ELT(names, 0, mkChar("idx"));
SET_STRING_ELT(names, 1, mkChar("x"));
SET_STRING_ELT(names, 2, mkChar("y"));
setAttrib(list_, R_NamesSymbol, names);
UNPROTECT(5);
return list_;
}
```
</details>
## Creating an R data.frame within C
Example code is included to create a data.frame completely within C and return it to R
<details>
<summary> Click to show/hide code </summary>
```{c eval = FALSE}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creating a data.frame within C and returning it to R
//
// 1. Create individual integer/real/whatever vectors
// 2. allocate a VECSXP of the correct size
// 3. assign each member into the data.frame
// 4. create names and assign them to the data.frame
// 5. set the class to "data.frame"
// 6. set rownames on the data.frame
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP create_data_frame_in_c_() {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Each member of the list gets allocated separately
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int n = 10;
SEXP idx_ = PROTECT(allocVector(INTSXP , n));
SEXP x_ = PROTECT(allocVector(REALSXP, n));
SEXP y_ = PROTECT(allocVector(REALSXP, n));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Assign some dummy values into the members
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for (int i = 0; i < n; i++) {
INTEGER(idx_)[i] = i + 1;
REAL(x_)[i] = i + 10;
REAL(y_)[i] = i + 100;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Allocate a data.frame
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP df_ = PROTECT(allocVector(VECSXP, 3));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Add columns to the data.frame
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SET_VECTOR_ELT(df_, 0, idx_);
SET_VECTOR_ELT(df_, 1, x_);
SET_VECTOR_ELT(df_, 2, y_);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Treat the VECSXP as a data.frame rather than a list
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SET_CLASS(df_, mkString("data.frame"));
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Set the names on the list.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP names = PROTECT(allocVector(STRSXP, 3));
SET_STRING_ELT(names, 0, mkChar("idx"));
SET_STRING_ELT(names, 1, mkChar("x"));
SET_STRING_ELT(names, 2, mkChar("y"));
setAttrib(df_, R_NamesSymbol, names);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Set the row.names on the list.
// Use the shortcut as used in .set_row_names() in R
// i.e. set rownames to c(NA_integer, -len) and it will
// take care of the rest. This is equivalent to rownames(x) <- NULL
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP rownames = PROTECT(allocVector(INTSXP, 2));
SET_INTEGER_ELT(rownames, 0, NA_INTEGER);
SET_INTEGER_ELT(rownames, 1, -n);
setAttrib(df_, R_RowNamesSymbol, rownames);
UNPROTECT(6);
return df_;
}
```
</details>
## Creating an R array within C
Example code is included to create an array completely within C and return it to R
<details>
<summary> Click to show/hide code </summary>
```{c eval = FALSE}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Creating an array within C and returning it to R
//
// 1. Create individual vector
// 2. Create an integer vector to hold the dimensions.
// 3. Set the dimension attribute on the vector
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SEXP create_array_in_c_() {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Each member of the list gets allocated separately
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int rows = 10;
int cols = 1;
int n = rows * cols;
SEXP arr_ = PROTECT(allocVector(INTSXP, n));
SEXP dim_ = PROTECT(allocVector(INTSXP, 2));
INTEGER(dim_)[0] = rows;
INTEGER(dim_)[1] = cols;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Assign some dummy values into the members
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
for (int i = 0; i < n; i++) {
INTEGER(arr_)[i] = i + 1;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Set the dimensions
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
setAttrib(arr_, R_DimSymbol, dim_);
UNPROTECT(2);
return arr_;
}
```
</details>
### Similar packages
This is one of a series of small demo packages for
calling other languages from R:
* [{simplec}](https://github.com/coolbutuseless/simplec) - calling C with `.C()`
* [{simplecall}](https://github.com/coolbutuseless/simplecall) - calling C with `.Call()`
* [{simplercpp}](https://github.com/coolbutuseless/simplercpp) - calling C++ with `{Rcpp}`
* [{simplefortran}](https://github.com/coolbutuseless/simplefortran) - calling Fortran with `.Fortran()`
## Installation
## Resources
* [David Vaughan's post - Now you C me](https://blog.davisvaughan.com/2019/03/02/now-you-c-me/)
* [Peter Dalgaard's Keynote from UseR 2004](http://www.ci.tuwien.ac.at/Conferences/useR-2004/Keynotes/Dalgaard.pdf)
discusses R's language interfaces
* [Using R — Calling C Code ‘Hello World’](http://mazamascience.com/WorkingWithData/?p=1067)
* [Hadley's Advanced R book chapter - 'Rs C interface'](http://adv-r.had.co.nz/C-interface.html)
* [Rcpp](https://cran.r-project.org/package=Rcpp)
## Acknowledgements
* R Core for developing and maintaining such a wonderful language.
* CRAN maintainers, for patiently shepherding packages onto CRAN and maintaining
the repository