I need to check the arity of a function in an Rcpp block at run time. What I would like to do is something akin to the following:
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;
}
However, the limited documentation I've seen for Rcpp does not seem to contain a function for checking the arity of an Rcpp::Function. Is there any way to do this?
So I solved this using a workaround that is a little bit clunky but after giving it some serious thought, this is the least clunky of three methods I tried implementing.
The method I ended up going with is checking the arity of the function on the R side using methods::formalArgs
, wrapping the (function, arity) pair in a list and passing that to the Rcpp function like so:
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;
}
As I mentioned, this is a bit clunky and it changes the signature of the funciton, which is not ideal. Another way of doing this would be to use the forgiveness-rather-than-permission approach and doing the control flow in a try-catch block, like so:
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;
}
This approach is less clunky, but the problem with it is that it also catches other exceptions that might arise within f
and silences them so they aren't passed to the user. It is possible that there are more fine-grained exceptions defined in Rcpp that would be able to catch the specific error of passing too many parameters, but if so I haven't found it.
Lastly, we could pass methods::formalArgs
to loglikelihood
and query the arity just before we need to use it, but I think this approach is the clunkiest of the three, because it requires us to pass formalArgs
around a lot.