From prototype to production, choosing the right R and C++ tool in Rcpp
Jul 6, 2025·
·
5 min read
Thiago de Paula Oliveira
1 Prototype at light-speed with inline
inline::cxxfunction() compiles, links and returns an R function from a character string, ideal for quick sketches or teaching.
library(inline)
library(Rcpp)##
## Attaching package: 'Rcpp'## The following object is masked from 'package:inline':
##
## registerPluginsrc <- '
// Declare two Rcpp numeric vectors, populated from the R arguments `a` and `b`
Rcpp::NumericVector xa(a), xb(b);
// Cache the lengths of those vectors in plain C++ ints for fast reuse
int na = xa.size(), nb = xb.size();
// Pre-allocate the output vector, with length (na + nb − 1) for a full
// discrete convolution
Rcpp::NumericVector out(na + nb - 1);
// Walk over every element of the first vector
for (int i = 0; i < na; ++i)
// Multiply the i-th element of `a` with every element of `b`
for (int j = 0; j < nb; ++j)
// Accumulate the product at the correct lag/index in the result
out[i + j] += xa[i] * xb[j];
// Return the filled result vector back to R
return out;
'
conv <- cxxfunction(signature(a = "numeric", b = "numeric"),
body = src, plugin = "Rcpp")
conv(1:4, 2:5)## [1] 2 7 16 30 34 31 20#> [1] 2 7 16 30 34 31 20Choose inline when:
- you need an answer now without creating extra files;
- you are benchmarking or exploring an API shape;
- you want to inject ad-hoc
#includeblocks or compiler flags.
1.1 Level-up with plugins and Rcpp attributes
1.1.1 4.1 Plugins
Plugins bolt extra headers and libraries onto an inline or attributes build—no manual -I/-L juggling.
The built-in RcppArmadillo plugin lets you write BLAS-backed linear algebra on the fly:
library(RcppArmadillo)
src <- '
// Armadillo + Rcpp headers are supplied automatically by the plugin
using namespace Rcpp;
/* ---- 1. Map the inputs -------------------------------------------------- */
Rcpp::NumericVector yr_(yr); // wrap SEXP as NumericVector
Rcpp::NumericMatrix Xr_(Xr); // wrap SEXP as NumericMatrix
int n = Xr_.nrow(); // rows = #obs
int k = Xr_.ncol(); // cols = #predictors
// zero-copy Armadillo views
arma::colvec y( yr_.begin(), yr_.size(), false );
arma::mat X( Xr_.begin(), n, k, false );
/* ---- 2. OLS -------------------------------------------------------------- */
arma::colvec coef = arma::solve(X, y);
arma::colvec res = y - X * coef;
double s2 = arma::dot(res, res) / (n - k);
arma::colvec se = arma::sqrt( s2 * arma::diagvec( arma::inv(X.t() * X) ) );
/* ---- 3. Ship results back to R ------------------------------------------ */
return List::create(_["coef"] = coef,
_["se"] = se,
_["df"] = n - k);
'
fastLm <- cxxfunction(signature(yr = "numeric", Xr = "numeric"),
body = src,
plugin = "RcppArmadillo")
# Usage
set.seed(123)
n <- 1000
k <- 3
X <- cbind(1, matrix(rnorm(n * k), n, k)) # 1st col = intercept
beta <- c(5, 0.8, -1.2, 0.5) # true coefficients
y <- X %*% beta + rnorm(n, sd = 2) # synthetic data
res <- fastLm(y, X)
str(res)## List of 3
## $ coef: num [1:4, 1] 4.984 0.797 -1.216 0.603
## $ se : num [1:4, 1] 0.0628 0.0636 0.0625 0.0642
## $ df : int 996fit <- lm(y ~ X[,2] + X[,3] + X[,4]) # lm() adds its own intercept
cbind(fastLm = res$coef,
lm = coef(fit))## lm
## (Intercept) 4.9844816 4.9844816
## X[, 2] 0.7974000 0.7974000
## X[, 3] -1.2162403 -1.2162403
## X[, 4] 0.6029614 0.6029614microbenchmark::microbenchmark(
fastLm = fastLm(y, X),
lm = { lm(y ~ X[,2] + X[,3] + X[,4]) },
times = 100
)## Unit: microseconds
## expr min lq mean median uq max neval cld
## fastLm 43.001 46.6515 64.69696 59.2515 80.051 130.201 100 a
## lm 856.101 923.0515 995.95507 944.1510 1004.351 1445.001 100 b1.1.2 4.2 Rcpp Attributes
- Attributes internalise inline’s magic behind
[[Rcpp::export]]. sourceCpp()compiles a file;cppFunction()compiles from a string;evalCpp()runs an expression, all while caching builds transparently.
cpp_src <- paste(
'#include <Rcpp.h>',
'using namespace Rcpp;',
'',
'// [[Rcpp::export]]',
'int fibonacci(int x) {',
' return (x < 2) ? x : fibonacci(x - 1) + fibonacci(x - 2);',
'}',
sep = "\n"
)
writeLines(cpp_src, "fibonacci.cpp") # main copyRcpp::sourceCpp("fibonacci.cpp")
fibonacci(20) #> 6765## [1] 6765Use attributes for almost everything once you move beyond exploratory work.
1.2 Quick decision guide
| Task | Reach for … |
|---|---|
| Package / production code | Rcpp Attributes |
| One-off sketches, benchmarks, Stack Overflow post | inline::cxxfunction() |
| Extra C++ libraries (Armadillo, Eigen, GSL, …) | Plugins (built-in or custom) |
Low-level debugging with gdb | Manual R CMD SHLIB + helper flags |
| Propagate C++ exceptions cleanly | Ensure BEGIN_RCPP/END_RCPP are present (added automatically by inline / Attributes) |
1.3 Take-aways
- Match R’s compiler or live with hard-to-trace crashes.
.Call()via Rcpp is the modern native interface—type, which is safe and header-driven.- inline and Attributes shrink the compile–link–load loop to a single line.
- Plugins keep code portable while tapping heavy
C++libraries. - BEGIN_RCPP/END_RCPP keep your
C++errors readable from R.
2 Reproducibility
sessionInfo()## R version 4.4.3 (2025-02-28 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 11 x64 (build 22631)
##
## Matrix products: default
##
##
## locale:
## [1] LC_COLLATE=English_United Kingdom.utf8
## [2] LC_CTYPE=English_United Kingdom.utf8
## [3] LC_MONETARY=English_United Kingdom.utf8
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United Kingdom.utf8
##
## time zone: Europe/London
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] RcppArmadillo_14.6.0-1 Rcpp_1.1.0 inline_0.3.21
##
## loaded via a namespace (and not attached):
## [1] microbenchmark_1.4.10 cli_3.6.2 knitr_1.50
## [4] TH.data_1.1-3 rlang_1.1.6 xfun_0.52
## [7] jsonlite_1.8.8 zoo_1.8-12 htmltools_0.5.8.1
## [10] sass_0.4.9 rmarkdown_2.29 grid_4.4.3
## [13] evaluate_1.0.4 jquerylib_0.1.4 MASS_7.3-64
## [16] fastmap_1.2.0 mvtnorm_1.2-5 yaml_2.3.10
## [19] lifecycle_1.0.4 bookdown_0.40 compiler_4.4.3
## [22] multcomp_1.4-28 codetools_0.2-20 sandwich_3.1-1
## [25] rstudioapi_0.17.1 blogdown_1.21 lattice_0.22-6
## [28] digest_0.6.35 R6_2.6.1 splines_4.4.3
## [31] Matrix_1.7-2 bslib_0.7.0 tools_4.4.3
## [34] survival_3.8-3 cachem_1.1.0Did you find this page helpful? Consider sharing it 🙌
3 How to cite this post
Oliveira, T. de Paula. (2025, August 9). From prototype to production, choosing the right R and C++ tool in Rcpp.
https://prof-thiagooliveira.netlify.app/post/rcpp-start-with-r/