Agent-almanac add-rcpp-integration

install
source · Clone the upstream repo
git clone https://github.com/pjt222/agent-almanac
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/es/skills/add-rcpp-integration" ~/.claude/skills/pjt222-agent-almanac-add-rcpp-integration-47a21e && rm -rf "$T"
manifest: i18n/es/skills/add-rcpp-integration/SKILL.md
source content

Añadir Integración con Rcpp

Integrar código C++ en un paquete R usando Rcpp para operaciones críticas de rendimiento.

Cuándo Usar

  • La función R es demasiado lenta y la elaboración de perfiles confirma un cuello de botella
  • Se necesita interactuar con bibliotecas C/C++ existentes
  • Implementar algoritmos que se benefician del código compilado (bucles, recursión)
  • Añadir RcppArmadillo para operaciones de álgebra lineal

Entradas

  • Obligatorio: Paquete R existente
  • Obligatorio: Función R a reemplazar o complementar con C++
  • Opcional: Biblioteca C++ externa con la que interactuar
  • Opcional: Si usar RcppArmadillo (predeterminado: Rcpp simple)

Procedimiento

Paso 1: Configurar la Infraestructura de Rcpp

usethis::use_rcpp()

Esto:

  • Crea el directorio
    src/
  • Añade
    Rcpp
    a LinkingTo e Imports en DESCRIPTION
  • Crea
    R/packagename-package.R
    con
    @useDynLib
    y
    @importFrom Rcpp sourceCpp
  • Actualiza
    .gitignore
    para los archivos compilados

Para RcppArmadillo:

usethis::use_rcpp_armadillo()

Esperado: Directorio

src/
creado, DESCRIPTION actualizado con
Rcpp
en LinkingTo e Imports, y
R/packagename-package.R
contiene la directiva
@useDynLib
.

En caso de fallo: Si

usethis::use_rcpp()
falla, crear manualmente
src/
, añadir
LinkingTo: Rcpp
e
Imports: Rcpp
a DESCRIPTION, y añadir
#' @useDynLib packagename, .registration = TRUE
y
#' @importFrom Rcpp sourceCpp
al archivo de documentación a nivel de paquete.

Paso 2: Escribir la Función C++

Crear

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;
}

Para 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;
}

Esperado: Archivo fuente C++ en

src/my_function.cpp
con anotación
// [[Rcpp::export]]
válida y comentarios de documentación estilo roxygen
//'
.

En caso de fallo: Verificar que el archivo usa

#include <Rcpp.h>
(o
<RcppArmadillo.h>
para Armadillo), que la anotación de exportación está en su propia línea directamente encima de la firma de la función, y que los tipos de retorno se corresponden con tipos válidos de Rcpp.

Paso 3: Generar RcppExports

Rcpp::compileAttributes()
devtools::document()

Esperado:

R/RcppExports.R
y
src/RcppExports.cpp
generados automáticamente.

En caso de fallo: Revisar los errores de sintaxis C++. Asegurarse de que la etiqueta

// [[Rcpp::export]]
está presente encima de cada función exportada.

Paso 4: Verificar la Compilación

devtools::load_all()

Esperado: El paquete compila y carga sin errores.

En caso de fallo: Revisar la salida del compilador para detectar errores. Problemas frecuentes:

  • Cabeceras de sistema faltantes: Instalar las bibliotecas de desarrollo
  • Errores de sintaxis: Los mensajes del compilador C++ apuntan a la línea
  • Falta el atributo
    Rcpp::depends
    para RcppArmadillo

Paso 5: Escribir Pruebas para el Código Compilado

test_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_))
})

Esperado: Las pruebas pasan, confirmando que la función C++ produce resultados idénticos al equivalente R y gestiona los casos límite (vectores vacíos, valores NA) correctamente.

En caso de fallo: Si las pruebas fallan con la gestión de NA, añadir comprobaciones explícitas de NA en el código C++ usando

NumericVector::is_na()
. Si las pruebas fallan con entrada vacía, añadir una cláusula de guarda para vectores de longitud cero al inicio de la función.

Paso 6: Añadir Script de Limpieza

Crear

src/Makevars
:

PKG_CXXFLAGS = -O2

Crear

cleanup
en la raíz del paquete (para CRAN):

#!/bin/sh
rm -f src/*.o src/*.so src/*.dll

Hacer ejecutable:

chmod +x cleanup

Esperado:

src/Makevars
establece los indicadores del compilador y el script
cleanup
elimina los objetos compilados. Ambos archivos existen en el nivel raíz del paquete.

En caso de fallo: Verificar que

cleanup
tiene permisos de ejecución (
chmod +x cleanup
) y que
Makevars
usa tabulaciones (no espacios) para la sangría si se añaden reglas estilo Makefile.

Paso 7: Actualizar .Rbuildignore

Asegurarse de que los artefactos compilados se gestionan correctamente:

^src/.*\.o$
^src/.*\.so$
^src/.*\.dll$

Esperado: Los patrones de

.Rbuildignore
evitan que los archivos objeto compilados se incluyan en el tarball del paquete, preservando los archivos fuente y Makevars.

En caso de fallo: Ejecutar

devtools::check()
y buscar NOTEs sobre archivos inesperados en
src/
. Ajustar los patrones de
.Rbuildignore
para excluir solo los archivos
.o
,
.so
y
.dll
.

Validación

  • devtools::load_all()
    compila sin advertencias
  • La función compilada produce resultados correctos
  • Las pruebas pasan para casos límite (NA, vacío, entradas grandes)
  • R CMD check
    pasa sin advertencias de compilación
  • Los archivos RcppExports están generados y confirmados
  • La mejora de rendimiento se confirma con benchmarks

Errores Comunes

  • Olvidar
    compileAttributes()
    : Hay que regenerar RcppExports tras modificar archivos C++
  • Desbordamiento de enteros: Usar
    double
    en lugar de
    int
    para valores numéricos grandes
  • Gestión de memoria: Rcpp gestiona la memoria automáticamente para los tipos Rcpp; no usar
    delete
    manualmente
  • Gestión de NA: C++ no conoce los NA de R. Comprobar con
    Rcpp::NumericVector::is_na()
  • Portabilidad entre plataformas: Evitar características C++ específicas de una plataforma. Probar en Windows, macOS y Linux.
  • Falta
    @useDynLib
    : La documentación a nivel de paquete debe incluir
    @useDynLib packagename, .registration = TRUE

Habilidades Relacionadas

  • create-r-package
    - configuración del paquete antes de añadir Rcpp
  • write-testthat-tests
    - probar las funciones compiladas
  • setup-github-actions-ci
    - CI debe tener la cadena de herramientas C++
  • submit-to-cran
    - los paquetes compilados necesitan verificaciones adicionales de CRAN