如何使用 Rcpp 处理 R 和 C++ 中调用约定的不匹配?

Dav*_*vid 0 c++ r calling-convention rcpp

我正在开发一个带有 C++ 函数的 R 包。我使用 Rcpp。编写和测试时一切正常。但是,当我完成并循环运行它时,我偶尔会收到以下警告和错误: 警告和错误

警告:“.Call”中的堆栈不平衡,78 然后是 77

警告:'{' 中的堆栈不平衡,75 然后是 74

警告:“.Call”中的堆栈不平衡,78 然后是 79

警告:'{' 中的堆栈不平衡,75 然后是 76

警告:“.Call”中的堆栈不平衡,78 然后是 77

警告:'{' 中的堆栈不平衡,75 然后是 74

所以我用谷歌搜索堆栈不平衡,我发现:

什么是堆栈不平衡?

本文指出堆栈不平衡通常是由语言之间调用约定的差异(不匹配)引起的。据我所知,callng约定是调用者函数删除局部变量还是被调用者函数删除的约定。

我发现了这个:

http://www.stats.uwo.ca/faculty/murdoch/software/compilingDLLs/

它说 R 正在使用cdecl而 C++ 使用stdcall.

“如果您的函数使用了stdcall而不是cdecl,则返回时可能会发生崩溃,因为您的参数将在它们已经消失后从堆栈中删除。”

如果我在声明中犯了任何错误,请告诉我。

这是我的 R 代码调用的 C++ 函数:

//[[Rcpp::export]]
int compute(SEXP p_in, Rcpp::NumericVector a) {
    Rcpp::XPtr<emcdf> p(p_in);
    p->cdf(a);
    return p->getResult();
}
//p_in is an external pointer(pointer to an emcdf object in C++)
Run Code Online (Sandbox Code Playgroud)

所以,我的问题是,我该如何解决这个问题?

我感谢您的帮助。

更多细节和代码 2.14.2017

谢谢各位的回答。这是更多代码:

//declaration of class "emcdf"
class emcdf{
public:
  explicit emcdf(Rcpp::NumericMatrix& x, int n);
  ~emcdf();
  void cdf(Rcpp::NumericVector& a);
  int getResult();
private:
  int num;
  int k;
  std::thread* t;
  std::vector<Rcpp::NumericMatrix*> data;
  int size;
  int* result;
  void (*ptr) (Rcpp::NumericMatrix*, Rcpp::NumericVector, int*);
  void find_func();
};

  //definition of class "emcdf"
  emcdf::emcdf(Rcpp::NumericMatrix& x, int n){
    //initialize members
    t = new std::thread[n];
    num = n;
    k = x.ncol();
    size = x.nrow()/num;
    result = new int[num];
    find_func();

    int i = 0;
    for(; i<num - 1; ++i)
      data.push_back(copy(x, i*size, size));

    data.push_back(copy(x, i*size, x.nrow() - i*size));

  }

  emcdf::~emcdf(){
    delete[] t;
    for(int i=0; i<num; ++i)
      delete data[i];
    delete[] result;
  }

  void emcdf::cdf(Rcpp::NumericVector& a){
    for(int i=0; i<num; ++i){
      t[i] = std::thread(*ptr, data[i], a, result + i);
    }
    for(int i=0; i<num; ++i)
      t[i].join();
  }

  int emcdf::getResult(){
    int sum = 0;
    for(int i=0; i<num; ++i)
      sum += result[i];
    return sum;
  }

//other functions
Rcpp::NumericMatrix* copy(Rcpp::NumericMatrix& x, int row, int nrow){

  NumericMatrix* out = new NumericMatrix(nrow, x.ncol());
  int firstRow = 0;
  while(firstRow < nrow){
    for(int j=0; j<x.ncol(); ++j){
      out->at(firstRow,j) = x.at(firstRow + row,j);
    }
    ++firstRow;
  }
  return out;
}

//makes "emcdf" pointer 
//[[Rcpp::export]]
RcppExport SEXP build(SEXP x_in, int num){

  Rcpp::NumericMatrix x(x_in);
  emcdf* em = new emcdf(x, num);
  Rcpp::XPtr<emcdf> p(em, true);
  return p;
}

//take pointer of "emcdf" from build() and compute cdf results
//[[Rcpp::export]]
int compute(SEXP& p_in, Rcpp::NumericVector& a){
  Rcpp::XPtr<emcdf> p(p_in);
  p->cdf(a);
  return p->getResult();
}
Run Code Online (Sandbox Code Playgroud)

compute() 中的参数 SEXP& p_in 是从 build() 的返回值传递过来的。在我的 R 代码中,我调用了 compute() 并得到了堆栈不平衡错误。它不会每次都出现,但是当连续调用超过 1000 次时,很可能会出错。

Kevin Ushey 建议:
“看起来这里的问题只是您试图在单独的线程中使用 R/Rcpp,但不幸的是这不起作用(因为您最终可能会从单独的线程触发垃圾收集器) .”

这是否意味着如果我避免在单独的线程中使用 Rcpp 函数/对象,例如,不使用*ptr我的线程运行的NumericMatrix ,这个错误将被修复?

谢谢你。

Kev*_*hey 5

不幸的是,我认为您的初步诊断走错了路。

当 R 报告堆栈不平衡时,这意味着内部保护堆栈(用于保护堆栈上的 R 对象免受垃圾收集器的影响)不平衡,表明某些 C/C++ 例程在某处不同步。这与调用约定无关。

Rcpp 通常在保护堆栈之外管理其对象的保护;它明确使用R_PreserveObject()R_ReleaseObject()API,它们不是基于堆栈的。

没有可重现的示例,很难说更多,但最可能的原因是您在尚未显示的单独代码中对PROTECT()/ 的调用不匹配UNPROTECT()