From agent-almanac
Add Rcpp or RcppArmadillo integration to an R package for high-performance C++ code. Covers setup, writing C++ functions, RcppExports generation, testing compiled code, and debugging. Use when an R function is too slow and profiling confirms a bottleneck, when you need to interface with existing C/C++ libraries, or when implementing algorithms (loops, recursion, linear algebra) that benefit from compiled code.
How this skill is triggered — by the user, by Claude, or both
Slash command
/agent-almanac:add-rcpp-integrationThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Integrate C++ code into an R package using Rcpp for performance-critical operations.
Integrate C++ code into an R package using Rcpp for performance-critical operations.
usethis::use_rcpp()
This:
src/ directoryRcpp to LinkingTo and Imports in DESCRIPTIONR/packagename-package.R with @useDynLib and @importFrom Rcpp sourceCpp.gitignore for compiled filesFor RcppArmadillo:
usethis::use_rcpp_armadillo()
Expected: src/ directory created, DESCRIPTION updated with Rcpp in LinkingTo and Imports, and R/packagename-package.R contains @useDynLib directive.
On failure: If usethis::use_rcpp() fails, manually create src/, add LinkingTo: Rcpp and Imports: Rcpp to DESCRIPTION, and add #' @useDynLib packagename, .registration = TRUE and #' @importFrom Rcpp sourceCpp to the package-level documentation file.
Create src/my_function.cpp:
#include <Rcpp.h>
using namespace Rcpp;
//' Compute cumulative sum efficiently
//'
//' @param x A numeric vector
//' @return A numeric vector of cumulative sums
//' @export
// [[Rcpp::export]]
NumericVector cumsum_cpp(NumericVector x) {
int n = x.size();
NumericVector out(n);
out[0] = x[0];
for (int i = 1; i < n; i++) {
out[i] = out[i - 1] + x[i];
}
return out;
}
For RcppArmadillo:
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]
//' Matrix multiplication using Armadillo
//'
//' @param A A numeric matrix
//' @param B A numeric matrix
//' @return The matrix product A * B
//' @export
// [[Rcpp::export]]
arma::mat mat_mult(const arma::mat& A, const arma::mat& B) {
return A * B;
}
Expected: C++ source file exists at src/my_function.cpp with valid // [[Rcpp::export]] annotation and roxygen-style //' documentation comments.
On failure: Verify the file uses #include <Rcpp.h> (or <RcppArmadillo.h> for Armadillo), that the export annotation is on its own line directly above the function signature, and that return types map to valid Rcpp types.
Rcpp::compileAttributes()
devtools::document()
Expected: R/RcppExports.R and src/RcppExports.cpp generated automatically.
On failure: Check C++ syntax errors. Ensure // [[Rcpp::export]] tag is present above each exported function.
devtools::load_all()
Expected: Package compiles and loads without errors.
On failure: Check compiler output for errors. Common issues:
Rcpp::depends attribute for RcppArmadillotest_that("cumsum_cpp matches base R", {
x <- c(1, 2, 3, 4, 5)
expect_equal(cumsum_cpp(x), cumsum(x))
})
test_that("cumsum_cpp handles edge cases", {
expect_equal(cumsum_cpp(numeric(0)), numeric(0))
expect_equal(cumsum_cpp(c(NA_real_, 1)), c(NA_real_, NA_real_))
})
Expected: Tests pass, confirming the C++ function produces identical results to the R equivalent and handles edge cases (empty vectors, NA values) correctly.
On failure: If tests fail on NA handling, add explicit NA checks in the C++ code using NumericVector::is_na(). If tests fail on empty input, add a guard clause for zero-length vectors at the top of the function.
Create src/Makevars:
PKG_CXXFLAGS = -O2
Create cleanup in package root (for CRAN):
#!/bin/sh
rm -f src/*.o src/*.so src/*.dll
Make executable: chmod +x cleanup
Expected: src/Makevars sets compiler flags and cleanup script removes compiled objects. Both files exist at the package root level.
On failure: Verify cleanup has execute permissions (chmod +x cleanup) and that Makevars uses tabs (not spaces) for indentation if adding Makefile-style rules.
Ensure compiled artifacts are handled:
^src/.*\.o$
^src/.*\.so$
^src/.*\.dll$
Expected: .Rbuildignore patterns prevent compiled object files from being included in the package tarball, while preserving source files and Makevars.
On failure: Run devtools::check() and look for NOTEs about unexpected files in src/. Adjust .Rbuildignore patterns to exclude only .o, .so, and .dll files.
devtools::load_all() compiles without warningsR CMD check passes with no compilation warningscompileAttributes(): Must regenerate RcppExports after changing C++ filesdouble instead of int for large numeric valuesdeleteRcpp::NumericVector::is_na()@useDynLib: The package-level doc must include @useDynLib packagename, .registration = TRUEcreate-r-package - package setup before adding Rcppwrite-testthat-tests - testing compiled functionssetup-github-actions-ci - CI must have C++ toolchainsubmit-to-cran - compiled packages need extra CRAN checksnpx claudepluginhub pjt222/agent-almanacScaffolds a new R package with DESCRIPTION, NAMESPACE, testthat, roxygen2, renv, Git, and GitHub Actions CI. Follows usethis conventions. For starting packages from scratch or converting scripts.
Use when code loads or uses mcptools (library(mcptools), mcptools::), connecting AI agents to R sessions via MCP, or exposing R as an MCP server for Claude Code or VS Code
Use when designing R function APIs, reviewing R code for design issues, writing functions for R packages, or evaluating argument ordering and naming decisions. Does NOT cover: style/linting, error handling (rlang-conditions), CLI output (r-lib:cli), testing (testing-r-packages), CRAN compliance (cran-extrachecks).