更新RcppEigen项目R包文档时遭遇‘System command 'R' failed’错误:using语句无法正常工作
最近我在维护一个仅向R导出单个C++函数的RcppEigen项目时,碰到了一个头疼的问题:用devtools::document(pkg = "~/pfexamplesinr/")或者roxygen2::roxygenize(roclets="rd", package.dir = "pfexamplesinr/")更新文档时,总是报错「System command 'R' failed」,追根溯源找到第一个错误是:
RcppExports.cpp:15:21: error: ‘Map’ was not declared in this scope
奇怪的是,直接用Rcpp::sourceCpp('pfexamplesinr/src/likelihoods.cpp')编译代码却完全正常,而且我尝试移除using语句、手动给Eigen类型加上Eigen::前缀后,这个问题居然消失了。我怀疑是不是#include和// [[Rcpp::depends(RcppEigen)]]的顺序搞反了?
相关代码与错误日志
项目中的C++代码
#include <RcppEigen.h> #include "svol_sisr_hilb.h" #include "resamplers.h" // [[Rcpp::depends(RcppEigen)]] // choose number of particles, and number of bits for inverse Hilbert curve map #define NP 500 #define NB 5 #define debug_mode false using Eigen::Map; using Eigen::MatrixXd; using Eigen::VectorXd; using hilb_sys_resamp_T = pf::resamplers::sys_hilb_resampler<NP,1,NB,double>; using svol_pfilter = svol_sisr_hilb<NP,NB, hilb_sys_resamp_T, double, debug_mode>; // helpful notes: // 1. // parameters passed to svol_pfilter() ctor are in the following order: phi, beta, sigma // 2. // uProposal will be dimension (time X (particles + 1)) // first NP columns will be used for state sampling // last column will be used for resampling at each time point // 3. // choosing NP or NB too large will result in stackoverflow // number of particles is set in two places: in the #define directive and also used in your R script // [[Rcpp::export]] double svolApproxLL(const Map<VectorXd> y, const Map<VectorXd> thetaProposal, const Map<MatrixXd> uProposal) { // construct particle filter object svol_pfilter pf(thetaProposal(0), thetaProposal(1), thetaProposal(2)); // order: phi, beta, sigma // iterate over the data double log_like(0.0); Eigen::Matrix<double,1,1> yt; std::array<Eigen::Matrix<double,1,1>, NP> uStateTransition; Eigen::Matrix<double,1,1> uResample; for(int time = 0; time < y.rows(); ++time){ // change types of inputs yt(0) = y(time); for(unsigned particle = 0; particle < NP; ++particle) { uStateTransition[particle] = uProposal.block(time,particle,1,1); } uResample(0) = uProposal(time,NP); // std::cout << yt.transpose() << "\n"; // for(unsigned int i = 0; i < NP; ++i) // std::cout << uStateTransition[i] << ", "; // std::cout << "\n----------\n"; // update particle filter and log-likelihood pf.filter(yt, uStateTransition, uResample); log_like += pf.getLogCondLike(); } //return es.eigenvalues(); return log_like; } // You can include R code blocks in C++ files processed with sourceCpp // (useful for testing and development). The R code will be automatically // run after the compilation. /*** R numTime <- 3 numParts <- 500 # make sure this agrees with NP u <- matrix(rnorm(numTime*(numParts+1)), ncol = numParts+1) params <- c(.9, 1, .1) # -1 < phi < 1, beta, sigma > 0 hist(replicate(100, svolApproxLL(rnorm(numTime), params, u))) */
错误日志片段
Error in (function (command = NULL, args = character(), error_on_status = TRUE, : System command 'R' failed, exit status: 1, stdout + stderr (last 10 lines): E> /home/taylor/R/x86_64-pc-linux-gnu-library/4.1/RcppEigen/include/Eigen/src/Core/MatrixBase.h:48:34: required from ‘class Eigen::MatrixBase<Eigen::Matrix<double, -1, 1> >’ E> /home/taylor/R/x86_64-pc-linux-gnu-library/4.1/RcppEigen/include/Eigen/src/Core/PlainObjectBase.h:98:7: required from ‘class Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1> >’ E> /home/taylor/R/x86_64-pc-linux-gnu-library/4.1/RcppEigen/include/Eigen/src/Core/Matrix.h:178:7: required from ‘class Eigen::Matrix<double, -1, 1>’ E> /home/taylor/R/x86_64-pc-linux-gnu-library/4.1/Rcpp/include/Rcpp/InputParameter.h:77:11: required from ‘class Rcpp::ConstReferenceInputParameter<Eigen::Matrix<double, -1, 1> >’ E> RcppExports.cpp:43:74: required from here E> /home/taylor/R/x86_64-pc-linux-gnu-library/4.1/RcppEigen/include/Eigen/src/Core/DenseCoeffsBase.h:5
问题根源与解决办法
你猜的完全没错,// [[Rcpp::depends(RcppEigen)]]的位置就是问题的核心。
为什么会出现这个问题?
当用devtools::document或roxygenize生成文档时,Rcpp会自动生成RcppExports.cpp文件,这个文件需要正确识别Eigen的依赖路径。如果把// [[Rcpp::depends(RcppEigen)]]放在#include <RcppEigen.h>之后,在生成导出代码的过程中,编译器可能还没加载RcppEigen的依赖,导致处理导出函数的类型(比如Map<VectorXd>)时,找不到Eigen的命名空间定义。
而sourceCpp能正常工作,是因为它会直接按文件里的指令顺序处理,即时加载依赖;但文档生成流程是先处理Rcpp的属性标记,再生成导出代码,顺序不对就会踩坑。
至于移除using Eigen::Map;手动加前缀能解决问题,本质是因为这样明确指定了命名空间,不需要依赖全局的using声明来找到Map类型,相当于绕开了依赖加载顺序的问题。
两种解决方式
方式1:调整指令顺序(推荐)
把// [[Rcpp::depends(RcppEigen)]]放在所有#include语句的最前面,确保Rcpp在处理文件时先加载依赖:
// [[Rcpp::depends(RcppEigen)]] #include <RcppEigen.h> #include "svol_sisr_hilb.h" #include "resamplers.h" // 后续代码保持不变
方式2:显式指定Eigen命名空间
如果不想调整顺序,就像你之前尝试的那样,移除所有using Eigen::XXX;语句,在用到Eigen类型的地方都加上Eigen::前缀。比如把函数参数从const Map<VectorXd> y改成const Eigen::Map<Eigen::VectorXd> y,代码里的MatrixXd、VectorXd也都加上Eigen::前缀。这样即使依赖加载顺序有小问题,编译器也能明确找到对应的类型定义。
内容的提问来源于stack exchange,提问作者Taylor

