在模板化 Rcpp 函数中调用另一个 cpp 函数

GoG*_*nzo 5 c++ r rcpp

我正在尝试在 Rcpp 中创建某种 sapply 函数,其工作原理如下:

  • apply_cpp_fun(x, fun, ...)function 接受两个参数:任何类型的向量x,和任何 cpp 函数fun,加上fun(例如bool na_rm)中所需的可选参数。在示例中,我保持简单,只是x和fun。

  • 我想fun应用于x(可能的fun输出 - bool、int、double、string)的选定元素。我不想申请在里面被多次调用apply_cpp_fun。

  • 的输出apply_cpp_fun是依赖于fun输出的任何类型的向量(可以不同于x)。fun被称为 n 次,以产生输出向量的每个元素res。

我正在尝试通过以下方式实现这一目标,但每次输出都变成了Rcpp::List而不是Rcpp::Vector<double>.

这是一个代码,我没有写整个代码apply_cpp_fun来使示例更短。如您所见,即使我通过了<double>function,模板也会将 Vector 描述为double (*)(Rcpp::Vector<14, Rcpp::PreserveStorage>)。

  #include <Rcpp.h>

  double cpp_sum(Rcpp::NumericVector x) {
    int n = x.size();
    double cursum = 0;

    for (int i = 0; i < n; i++) {
      cursum += x(i);
    }

     return cursum;
  }

  template <int ITYPE, typename ftype>
  Rcpp::Vector<Rcpp::traits::r_sexptype_traits<ftype>::rtype>
  apply_cpp_fun(Rcpp::Vector<ITYPE>& x,
                ftype fun) {

    int n = x.size();
    double xx = 5.0;

    # typenames
    Rcpp::Rcout << "type of xx: " << demangle(typeid(xx).name()).c_str() << std::endl;
    Rcpp::Rcout << "function type: " << demangle(typeid(ftype).name()).c_str() << std::endl;
    const int OTYPE = Rcpp::traits::r_sexptype_traits<ftype>::rtype;
    Rcpp::Rcout << "SEXP type: " << OTYPE << std::endl;

    # apply fun n-times
    Rcpp::Vector<OTYPE> res(n);
    for (int i = 0; i < n; i++) {
      res(i) = fun(x);
    }

    return res;  # return vector
  }

  // [[Rcpp::export]]
  SEXP cumsum_cpp(Rcpp::NumericVector x) {
    return apply_cpp_fun(x, cpp_sum);
  }

Run Code Online (Sandbox Code Playgroud)

调用函数查看结果

cumsum_cpp(as.numeric(1:2))
# type of xx: double
# function type: double (*)(Rcpp::Vector<14, Rcpp::PreserveStorage>)
# SEXP type: 19
# [[1]]
# NULL
#
# [[2]]
# NULL


Run Code Online (Sandbox Code Playgroud)

如何解决此问题以保持应用程序对输入类型和输出的灵活性?感谢您的任何建议。

All*_*ron 1

以下实现方法apply_cpp_fun使用捕获要应用的函数的输出类型的技巧decltype,并将其转换为适当的 SEXP 类型Rcpp::traits::r_sexptype_traits<T>::rtype。如果这是作为 a 捕获的,constexpr int那么它可以用作创建Rcpp:Vector适当类型的 a 的模板参数。

这样做的好处是您不需要将任何模板参数传递给apply_cpp_fun.

#include <Rcpp.h>

template<typename Func, typename Input>
SEXP apply_cpp_fun(Input& v, Func f)
{
  int n = v.size();
  constexpr int t = Rcpp::traits::r_sexptype_traits<decltype(f(v, n))>::rtype;
  Rcpp::Vector<t> result(n);

  for (int i = 0; i < n; i++) result(i) = f(v, i);
  return result;
}
Run Code Online (Sandbox Code Playgroud)

假设我们要应用以下函数:

#include <string>
#include <vector>
// [[Rcpp::plugins("cpp11")]]

Rcpp::String as_string(Rcpp::NumericVector const& x, int i) {
  return std::to_string(x[i]);
}

double as_numeric(Rcpp::NumericVector const& x, int i) {
  return x[i];
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用它们apply_cpp_fun并导出到 R,如下所示:

// [[Rcpp::export]]
Rcpp::NumericVector test1_tmpl(Rcpp::NumericVector x) 
{
  return apply_cpp_fun(x, as_numeric);
}

// [[Rcpp::export]]
Rcpp::StringVector test2_tmpl(Rcpp::NumericVector x) 
{
  return apply_cpp_fun(x, as_string);
}
Run Code Online (Sandbox Code Playgroud)

现在在 R 中:

test1_tmpl(1:5)
# [1] 1 2 3 4 5

test2_tmpl(1:5)
# [1] "1.000000" "2.000000" "3.000000" "4.000000" "5.000000"
Run Code Online (Sandbox Code Playgroud)

笔记

尽管OP接受了我最初的答案,即使用std::类型并简单地使用Rcpp的本机转换将它们传入和传出,但@KonradRudolph指出这涉及不必要的副本。经过OP的进一步澄清和建议后,我在OP的许可下更改了对上述内容的答案,并使用了OP自己的答案中给出的示例。