使用Xptr和Function调用Rcpp函数-仅xptr情况有效

时间:2018-07-10 21:51:04

标签: r rcpp r-package

我正在尝试开发一个程序包,其中需要从用户那里输入一个函数(可以使用Rcpp或在R中定义),然后将其发送到另一个函数中(在程序包内) struct并在那里进行处理。

当我使用Rcpp::Xptr(即函数指针)时,代码有效,但对于Rcpp::Function则无效。为用户使用Rcpp::Function的好处是,他们将能够在R中定义函数(尽管会损失很多性能)。

首先起作用的是

#include <Rcpp.h>
using namespace Rcpp;

// define the structure
struct xptr_data{
  SEXP xptr;
};

// a minimal function (user-defined)
// [[Rcpp::export]]
NumericVector timesTwo(NumericVector x) {
  return x * 2;
}

// pointer to function defined
typedef NumericVector (*funcPtr) (NumericVector y);

// [[Rcpp::export]]
XPtr<funcPtr> putFunPtrInXPtr() {

  XPtr<funcPtr> rhs_ptr(new funcPtr(&timesTwo), false); 
  return rhs_ptr;
}

// this function will be in the package
NumericVector call_by_xptr_struct(NumericVector y, void* user_data){

  struct xptr_data *my_rhs_ptr = (struct xptr_data*)user_data;
  SEXP xpsexp = (*my_rhs_ptr).xptr;

  // use function pointer to get the derivatives
  XPtr<funcPtr> rhs_xptr(xpsexp);
  funcPtr rhs_fun = *rhs_xptr;

  // use the function to calculate value of RHS ----
  return(rhs_fun(y));
}


// using xptr to evaluate function - this will be exported
// from the package
//[[Rcpp::export]]
NumericVector xptr_call_struct(NumericVector y, SEXP xpsexp){

  struct xptr_data my_xptr = {NULL};

  my_xptr.xptr = xpsexp;
  return call_by_xptr_struct(y, (void*)&my_xptr);
}

/*** R    
rhs_ptr <- putFunPtrInXPtr()

xptr_call_struct(c(1,2), rhs_ptr)
[1] 2 4
*/ 

什么不起作用,

如果函数是在R中定义的,而我直接使用Rcpp::Function,则会使整个R会话崩溃,

#include <Rcpp.h>
using namespace Rcpp;

// define the function based structure
struct func_data{
  Function func;
};

// processes the input function 
NumericVector call_by_func_struct(NumericVector y, void* user_data){

  struct func_data *my_rhs_fun = (struct func_data*)user_data;
  Function func = (*my_rhs_fun).func;

  return(func(y));
}

// this will be exported from the package
//[[Rcpp::export]]
NumericVector func_call_struct(NumericVector y, Function func){

  struct func_data my_func = {NULL};

  my_func.func = func;
  return call_by_func_struct(y, (void*)&my_func);
}

/*** R
timesThree <- function(y){

  y <- 3 * y
  y
}

*/

上面的代码可以很好地编译,但是当我调用函数func_call_struct(c(1,2), timesThree))时,它将使整个R会话崩溃。

有关R为何崩溃以及如何输入R中定义的函数的任何指导都将非常有帮助。

此外,还有任何方法可以传递在Rcpp中定义的输入函数(例如,上面的timesTwo)而不是它们的Xptr。我认为对于最终用户(因为他们不必生成函数指针),在不牺牲Rcpp随附的速度的情况下,这将稍微减少一些混乱。

1 个答案:

答案 0 :(得分:3)

如果您initialize the struct使用可用的Function,它会起作用:

[...]
// this will be exported from the package
//[[Rcpp::export]]
NumericVector func_call_struct(NumericVector y, Function func){

  struct func_data my_func = {func};
  return call_by_func_struct(y, (void*)&my_func);
}

关于您的其他问题:如果不使用外部指针,我看不到在R中存储C ++函数指针的可能性。您能否提供一个从函数指针创建外部指针的辅助函数(可能为wrap())?遵循以下原则:

#include <Rcpp.h>
using namespace Rcpp;

// define the structure
struct xptr_data{
  SEXP xptr;
};

// pointer to function defined
typedef NumericVector (*funcPtr) (NumericVector y);


// this function will be in the package
NumericVector call_by_xptr_struct(NumericVector y, void* user_data){

  struct xptr_data *my_rhs_ptr = (struct xptr_data*)user_data;
  SEXP xpsexp = (*my_rhs_ptr).xptr;

  // use function pointer to get the derivatives
  XPtr<funcPtr> rhs_xptr(xpsexp);
  funcPtr rhs_fun = *rhs_xptr;

  // use the function to calculate value of RHS ----
  return(rhs_fun(y));
}


// using xptr to evaluate function - this will be exported
// from the package
//[[Rcpp::export]]
NumericVector xptr_call_struct(NumericVector y, SEXP xpsexp){

  struct xptr_data my_xptr = {xpsexp};
  return call_by_xptr_struct(y, (void*)&my_xptr);
}


// function in package with only C++ API
XPtr<funcPtr> wrapFunPtr(funcPtr& f) {
  XPtr<funcPtr> rhs_ptr(&f, false); 
  return rhs_ptr;
}



// user-defined functions
NumericVector timesTwo(NumericVector x) {
  return x * 2;
}

// [[Rcpp::export]]
XPtr<funcPtr> putFunPtrInXPtr() {
  static funcPtr f = &timesTwo;
  return wrapFunPtr(f);
}



/*** R    
rhs_ptr <- putFunPtrInXPtr()

xptr_call_struct(c(1,2), rhs_ptr)
*/ 

用户将提供最后两个功能。使用C ++ 11可以像这样简化:

// [[Rcpp::export]]
XPtr<funcPtr> putFunPtrInXPtr() {
  static funcPtr timesTwo{ [](NumericVector x) -> NumericVector { return x * 2; } };
  return wrapFunPtr(timesTwo); 
}

尽管在这种情况下,使用它可能就足够了

// [[Rcpp::export]]
XPtr<funcPtr> putFunPtrInXPtr() {
  static funcPtr timesTwo{ [](NumericVector x) -> NumericVector { return x * 2; } };
  XPtr<funcPtr> rhs_ptr(timesTwo, false); 
  return rhs_ptr;
}

在包装中不需要wrapFunPtr。用户必须提供一个功能,其中包括一些样板代码以及lambda表达式中的实际“肉”。