-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathREADME.Rmd
123 lines (93 loc) · 3.9 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
---
output: github_document
---
<!-- README.md is generated from README.Rmd. Please edit that file -->
```{r, echo = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "README-"
)
```
# RcppXPtrUtils: XPtr Add-Ons for 'Rcpp'
[![Build Status](https://github.com/Enchufa2/RcppXPtrUtils/actions/workflows/build.yml/badge.svg)](https://github.com/Enchufa2/RcppXPtrUtils/actions/workflows/build.yml)
[![Coverage Status](https://codecov.io/gh/Enchufa2/RcppXPtrUtils/branch/master/graph/badge.svg)](https://app.codecov.io/gh/Enchufa2/RcppXPtrUtils)
[![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/RcppXPtrUtils)](https://cran.r-project.org/package=RcppXPtrUtils)
[![Downloads](https://cranlogs.r-pkg.org/badges/RcppXPtrUtils)](https://cran.r-project.org/package=RcppXPtrUtils)
The **RcppXPtrUtils** package provides the means to compile user-supplied C++ functions with 'Rcpp' and retrieve an XPtr that can be passed to other C++ components.
## Installation
Install the release version from CRAN:
```{r, eval=FALSE}
install.packages("RcppXPtrUtils")
```
The installation from GitHub can be done with the [remotes](https://cran.r-project.org/package=remotes) package:
```{r, eval=FALSE}
remotes::install_github("Enchufa2/RcppXPtrUtils")
```
## Use case
Let's suppose we have a package with a core written in C++, connected to an R API with `Rcpp`. It accepts a user-supplied R function to perform some processing:
```{r, engine='Rcpp', eval=FALSE}
#include <Rcpp.h>
using namespace Rcpp;
template <typename T>
NumericVector core_processing(T func, double l) {
double accum = 0;
for (int i=0; i<1e3; i++)
accum += sum(as<NumericVector>(func(3, l)));
return NumericVector(1, accum);
}
// [[Rcpp::export]]
NumericVector execute_r(Function func, double l) {
return core_processing<Function>(func, l);
}
```
But calling R from C++ is slow, so we can think about improving the performance by accepting a compiled function. In order to do this, the core can be easily extended to accept an `XPtr` to a compiled function:
```{r, engine='Rcpp', eval=FALSE}
typedef SEXP (*funcPtr)(int, double);
// [[Rcpp::export]]
NumericVector execute_cpp(SEXP func_, double l) {
funcPtr func = *XPtr<funcPtr>(func_);
return core_processing<funcPtr>(func, l);
}
```
```{r, engine='Rcpp', echo=FALSE}
#include <Rcpp.h>
using namespace Rcpp;
template <typename T>
NumericVector core_processing(T func, double l) {
double accum = 0;
for (int i=0; i<1e3; i++)
accum += sum(as<NumericVector>(func(3, l)));
return NumericVector(1, accum);
}
// [[Rcpp::export]]
NumericVector execute_r(Function func, double l) {
return core_processing<Function>(func, l);
}
typedef SEXP (*funcPtr)(int, double);
// [[Rcpp::export]]
NumericVector execute_cpp(SEXP func_, double l) {
funcPtr func = *XPtr<funcPtr>(func_);
return core_processing<funcPtr>(func, l);
}
```
To easily leverage this feature, the `RcppXPtrUtils` package provides `cppXPtr()`, which compiles a user-supplied C++ function using `Rcpp::cppFunction()` and returns an `XPtr`:
```{r}
# compile the code above
# Rcpp::sourceCpp(code='...')
library(RcppXPtrUtils)
func_r <- function(n, l) rexp(n, l)
func_cpp <- cppXPtr("SEXP foo(int n, double l) { return rexp(n, l); }")
microbenchmark::microbenchmark(
execute_r(func_r, 1.5),
execute_cpp(func_cpp, 1.5)
)
```
The object returned by `cppXPtr()` is just an `externalptr` wrapped into an object of class `XPtr`, which stores the signature of the function. If you are a package author, you probably want to re-export `cppXPtr()` and ensure that user-supplied C++ functions comply with the internal signatures in order to avoid runtime errors. This can be done with the `checkXPtr()` function:
```{r, error=TRUE}
func_cpp
checkXPtr(func_cpp, "SEXP", c("int", "double")) # returns silently
checkXPtr(func_cpp, "int", c("int", "double"))
checkXPtr(func_cpp, "SEXP", c("int"))
checkXPtr(func_cpp, "SEXP", c("double", "int"))
```