有没有办法检查 Rcpp::Function 的数量?

问题描述

我需要在运行时检查 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}} 大约很多。