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

Is there a way to check the arity of 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 并获取(任意数量的) SEXP 个可以是函数的对象。所以所有这一切......回到 R API 它可能有也可能没有这样的访问器,它可能是也可能不是 public 并且应该供除 R 本身以外的任何人使用。

这些天我们用 R 注册编译函数(通常在文件 src/init.c 或类似文件中),其中这个参数数量 作为第二个参数传递给 (超出函数调用名称)进行注册时。这对我来说表明它是不可发现的。

所以我使用了一个有点笨拙的解决方法解决了这个问题,但在认真考虑之后,这是我尝试实施的三种方法中最不笨拙的。

我最终采用的方法是使用 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::List params, 
                     SEXP i, Rcpp::RObject custom_function) {

    Rcpp::Function f = Rcpp::as<Rcpp::Function>(custom_function);

    double res = 0.0;
    try {
        res = Rcpp::as<double>(f(data, param, i));
    } catch (const std::exception &e) {
        res = Rcpp::as<double>(f(data, param));
    }
    return res;
}

这种方法不那么笨拙,但它的问题是它还会捕获 f 中可能出现的其他异常并使它们静音,这样它们就不会传递给用户。有可能Rcpp中定义了更细粒度的异常,能够捕获传递太多参数的特定错误,但如果是这样我还没有找到。

最后,我们可以将 methods::formalArgs 传递给 loglikelihood 并在我们需要使用它之前查询它,但我认为这种方法是三种方法中最笨拙的,因为它需要我们经常通过 formalArgs