计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
add-rcpp-integration【效率与协作】向 R 包添加 Rcpp 或 RcppArmadillo 集成以实现高性能 C++ 代码。涵盖设置、编写 C++ 函数、RcppExports 生成、编译代码测试和调试。适用于 R 函数过慢且性能分析 确认存在瓶颈时、需要与现有 C/C++ 库交互时,或实现受益于编译代码的算法(循环、 递归、线性代数)时。
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
使用 Rcpp 将 C++ 代码集成到 R 包中,用于性能关键操作。
usethis::use_rcpp()
此操作会:
src/ 目录Rcpp 添加到 LinkingTo 和 Imports@useDynLib 和 @importFrom Rcpp sourceCpp 的 R/packagename-package.R.gitignore 以排除编译文件对于 RcppArmadillo:
usethis::use_rcpp_armadillo()
预期结果: src/ 目录已创建,DESCRIPTION 中 Rcpp 已添加到 LinkingTo 和 Imports,R/packagename-package.R 包含 @useDynLib 指令。
失败处理: 如果 usethis::use_rcpp() 失败,手动创建 src/,在 DESCRIPTION 中添加 LinkingTo: Rcpp 和 Imports: Rcpp,并在包级文档文件中添加 #' @useDynLib packagename, .registration = TRUE 和 #' @importFrom Rcpp sourceCpp。
创建 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;
}
对于 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;
}
预期结果: C++ 源文件存在于 src/my_function.cpp,具有有效的 // [[Rcpp::export]] 注解和 roxygen 风格的 //' 文档注释。
失败处理: 验证文件使用 #include <Rcpp.h>(或 Armadillo 使用 <RcppArmadillo.h>),导出注解位于函数签名正上方的单独行,且返回类型映射到有效的 Rcpp 类型。
Rcpp::compileAttributes()
devtools::document()
预期结果: R/RcppExports.R 和 src/RcppExports.cpp 自动生成。
失败处理: 检查 C++ 语法错误。确保每个导出函数上方都有 // [[Rcpp::export]] 标签。
devtools::load_all()
预期结果: 包编译并加载,无错误。
失败处理: 检查编译器输出中的错误。常见问题:
Rcpp::depends 属性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_))
})
预期结果: 测试通过,确认 C++ 函数产生与 R 等价物相同的结果,并正确处理边界情况(空向量、NA 值)。
失败处理: 如果 NA 处理测试失败,在 C++ 代码中使用 NumericVector::is_na() 添加显式 NA 检查。如果空输入测试失败,在函数顶部添加零长度向量的保护子句。
创建 src/Makevars:
PKG_CXXFLAGS = -O2
在包根目录创建 cleanup(用于 CRAN):
#!/bin/sh
rm -f src/*.o src/*.so src/*.dll
设为可执行:chmod +x cleanup
预期结果: src/Makevars 设置编译器标志,cleanup 脚本删除编译对象。两个文件都存在于包根目录级别。
失败处理: 验证 cleanup 具有执行权限(chmod +x cleanup),且如果添加 Makefile 风格的规则,Makevars 使用制表符(而非空格)缩进。
确保编译产物被正确处理:
^src/.*\.o$
^src/.*\.so$
^src/.*\.dll$
预期结果: .Rbuildignore 模式阻止编译对象文件被包含在包 tarball 中,同时保留源文件和 Makevars。
失败处理: 运行 devtools::check() 并查找关于 src/ 中意外文件的 NOTE。调整 .Rbuildignore 模式以仅排除 .o、.so 和 .dll 文件。
devtools::load_all() 编译无警告R CMD check 通过,无编译警告compileAttributes():更改 C++ 文件后必须重新生成 RcppExportsdouble 而不是 intdeleteRcpp::NumericVector::is_na() 检查@useDynLib:包级文档必须包含 @useDynLib packagename, .registration = TRUEcreate-r-package — 添加 Rcpp 之前的包设置write-testthat-tests — 测试编译函数setup-github-actions-ci — CI 必须有 C++ 工具链submit-to-cran — 编译包需要额外的 CRAN 检查