问题描述
我需要在运行时检查 Rcpp 块中函数的数量。我想做的是类似于以下内容:
double loglikelihood(Rcpp::List data,Rcpp::List params,SEXP i,Rcpp::RObject custom_function) {
Rcpp::Function f = Rcpp::as<Rcpp::Function>(custom_function);
double res = 0.0;
if (arity(f) == 3) {
res = Rcpp::as<double>(f(data,param,i));
} else if (arity(f) == 2) {
res = Rcpp::as<double>(f(data,param));
}
return res;
}
然而,我看到的关于 Rcpp 的有限文档似乎没有包含用于检查 Rcpp::Function 数量的函数。有没有办法做到这一点?
解决方法
“有限的文档”(目前仅 10 个 pdf 小插曲)告诉您,除其他外,我们从 R 本身获得的只是 .Call()
返回 SEXP
并采用(任意数量的){ {1}} 可以是函数的对象。所以所有这一切......都可以追溯到 R API,它可能有也可能没有这样的访问器,该访问器可能是也可能不是公共的,并且应该被除 R 本身之外的任何人使用。 >
这些天我们用 R 注册编译函数(通常在文件 SEXP
或类似文件中),其中这个数量的参数被传递作为第二个参数(超出函数调用名称)在进行注册时。这向我表明它是不可发现的。
所以我使用了一个有点笨拙的变通方法解决了这个问题,但经过深思熟虑后,这是我尝试实施的三种方法中最不笨拙的方法。
我最终采用的方法是使用 methods::formalArgs
检查 R 端函数的数量,将 (function,arity) 对包装在列表中并将其传递给 Rcpp 函数,如下所示:
double loglikelihood(Rcpp::List data,Rcpp::List params,SEXP i,Rcpp::RObject custom_function) {
Rcpp::List l = Rcpp::as<Rcpp::List>(custom_function);
Rcpp::Function f = Rcpp::as<Rcpp::Function>(l[0]);
int arity = l[1];
double res = 0.0;
if (arity == 3) {
res = Rcpp::as<double>(f(data,param,i));
} else if (arity == 2) {
res = Rcpp::as<double>(f(data,param));
}
return res;
}
正如我所提到的,这有点笨拙,它改变了函数的签名,这并不理想。另一种方法是使用宽恕而不是许可方法并在 try-catch 块中执行控制流,如下所示:
double loglikelihood(Rcpp::List data,Rcpp::RObject custom_function) {
Rcpp::Function f = Rcpp::as<Rcpp::Function>(custom_function);
double res = 0.0;
try {
res = Rcpp::as<double>(f(data,i));
} catch (const std::exception &e) {
res = Rcpp::as<double>(f(data,param));
}
return res;
}
这种方法不那么笨拙,但它的问题在于它还会捕获 f
中可能出现的其他异常并将它们静音,以便它们不会传递给用户。可能是Rcpp中定义了更多细粒度的异常,能够捕捉到传递过多参数的具体错误,但如果是这样我还没有找到。
最后,我们可以将 methods::formalArgs
传递给 loglikelihood
并在需要使用它之前查询 arity,但我认为这种方法是三种方法中最笨拙的,因为它需要我们传递 { {1}} 大约很多。