Rcpp 模块:具有相同数量参数的公开构造函数的验证器函数

Rcpp modules: Validator function for exposed constructors with same number of parameters

我想公开一个带有 Rcpp 模块的 C++ class,它有两个具有相同数量参数的构造函数。如 Rcpp modules 小插图中所述,这可以通过使用验证器函数作为 .constructor 的第二个参数来实现,类型为

typedef bool (*ValidConstructor)(SEXP*,int);

有人可以举例说明应该如何使用它吗?在小插图中,只有一个 TODO 注释,其中应包含一个示例。

免责声明,我以前没有亲自使用过它,以下似乎有效:

#include <Rcpp.h>
using namespace Rcpp;

class Example {
public:
    Example(int x_, int y_)
        : x(x_), y(y_)
    {
        Rcout << __PRETTY_FUNCTION__ << "\n";
    }

    Example(std::string x_, std::string y_)
        : x(x_.size()), y(y_.size())
    {
        Rcout << __PRETTY_FUNCTION__ << "\n";
    }

    int add() const
    { return x + y; }

private:
    int x, y;
};

bool validate_int_int(SEXP* args, int nargs)
{ return TYPEOF(args[0]) == INTSXP && TYPEOF(args[1]) == INTSXP; }

bool validate_string_string(SEXP* args, int nargs)
{ return TYPEOF(args[0]) == STRSXP && TYPEOF(args[1]) == STRSXP; }

RCPP_MODULE(ExampleMod) {

    class_<Example>("Example")
        .constructor<int, int>(
            "(int, int) constructor",
            validate_int_int
        )
        .constructor<std::string, std::string>(
            "(string, string) constructor",
            validate_string_string
        )
        .method("add", &Example::add)
    ;
}

从 R 测试,

ex.int <- new(Example, 1L, 2L)
# Example::Example(int, int)

ex.string <- new(Example, "one", "two")
# Example::Example(std::string, std::string)

ex.int$add()
# [1] 3

ex.string$add()
# [1] 6 

validate_int_intvalidate_string_string 中,我们只是根据相应构造函数的签名测试输入 SEXP 类型。

要跟进 nrussell 的回答,您可能需要从模板生成验证器,即

template <typename T0, typename T1>
bool typed_valid( SEXP* args, int nargs ){
  return nargs == 2 && Rcpp::is<T0>(args[0]) && Rcpp::is<T1>(args[1]) ;
}

您将用作:

.constructor<int, int>( & typed_valid<int,int> )

显然 typed_valid 函数模板可以直接用可变参数模板进行概括。

沿着之前答案的脚步,我发现在 cpp.

中使用可变参数模板有可能存在通用验证器函数

实施比我预期的要短,但有许多技术细节超出了本文的范围post。

template <typename... Types>
bool universal_validator(SEXP* args, int nargs) {
  return universal_validator<Types...>(args, nargs, 0);
}

template <typename T = void, typename... Types>
bool universal_validator(SEXP* args, int nargs, int idx) {
  if (idx>=nargs) return false;

  // optional type traits
  typedef typename Rcpp::traits::remove_const_and_reference<T>::type _Tp;

  return Rcpp::is<_Tp>(args[idx]) && universal_validator<Types...>(args, nargs, idx+1);
}

template <>
bool universal_validator<>(SEXP* args, int nargs, int idx) {
  return nargs == idx;
}

这个universal_validator的用法很简单。给定Exampleclass,RCPP_MODULE中的构造函数将变成如下

RCPP_MODULE(example_module) {

  Rcpp::class_<Example>("Example")
  

  .constructor<int, int>("(int, int) constructor", 
                         universal_validator<int, int>)

  .constructor<std::string, std::string> ("(string, string) constructor",
                                          universal_validator<std::string, std::string>)
  .method("add", &Example::add)
  ;
}

只需将与构造函数相同的参数类型放入验证器模板中,例如universal_validator<int, int> 会完成这项工作。只要为任何类型 T.

定义了 Rcpp::is<T>,这就可以工作

来自github的源代码 SEXP newInstance( SEXP* args, int nargs ) 表示验证器必须同时检查 SEXP*nargs 的类型。这就是为什么在所有函数中不仅要检查类型,还要检查 SEXP* 的索引。