创建两个具有相同名称但参数类型不同的R函数 [英] Create two R functions with same name but different type of argument

查看:95
本文介绍了创建两个具有相同名称但参数类型不同的R函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Rcpp创建一个包,并且我想要一个函数,可以接受 std :: string int 作为参数.我该怎么办?

I am creating a package with Rcpp and I want to have a function than can accept as argument a std::string or a int. How can I do it?

我具有以下功能:

int myFunction(std::string varname);
RcppExport SEXP myFunction(SEXP varname) {
BEGIN_RCPP
    Rcpp::RObject __result;
    Rcpp::RNGScope __rngScope;
    std::string myVarname = as<std::string>(varname);
    __result = Rcpp::wrap(myFunction(myVarname));
    return __result;
END_RCPP
}

int myFunction(int varname);
RcppExport SEXP myFunction(SEXP varname) {
BEGIN_RCPP
    Rcpp::RObject __result;
    Rcpp::RNGScope __rngScope;
    int myVarname = as<int>(varname);
    __result = Rcpp::wrap(myFunction(myVarname));
    return __result;
END_RCPP
}

我已经实现了两个c ++函数(一个允许输入 int ,另一个允许输入 std :: string )

I have implemented the two c++ functions (one admits an int and the other one admits a std::string)

在定义R函数的文件中:

In the file where I define the R functions I have:

myFunction <- function(varname) {
    .Call('myFunction', PACKAGE = 'myPackage', varname)
}

构建软件包时,出现以下错误:

When I build my package I got the following error:

RcppExports.cpp:78:17: error: redefinition of ‘SEXPREC* myFunction(SEXP)’
RcppExport SEXP myFunction(SEXP varname) {
             ^
RcppExports.cpp:67:17: note: ‘SEXPREC* myFunction(SEXP)’ previously defined here
RcppExport SEXP myFunction(SEXP varname) {

推荐答案

正如Dirk在评论中指出的,可以通过从(单个)导出函数中分派适当的实现函数来完成此操作.典型的方法包括 switch 语句和 TYPEOF 宏,如下所示:

As Dirk noted in the comments, this can by done by dispatching the appropriate implementation function from within the (single) exported function. The typical approach involves a switch statement and the TYPEOF macro, as shown below:

#include <Rcpp.h>

struct fallthrough {};

template <typename T>
int overloaded_impl(const T& t) {
    return -1;
}

template <>
int overloaded_impl<std::string>(const std::string& x) {
    return x.size();
}

template <>
int overloaded_impl<int>(const int& x) {
    return x * 2;
}

// [[Rcpp::export]]
int overloaded(SEXP x) {
    switch (TYPEOF(x)) {
        case INTSXP: {
            return overloaded_impl<int>(INTEGER(x)[0]);
        }
        case REALSXP: {
            return overloaded_impl<int>((int)(REAL(x)[0]));
        }
        case STRSXP: {
            std::string tmp = CHAR(STRING_ELT(x, 0));
            return overloaded_impl<std::string>(tmp);
        }
        default: {
            Rcpp::warning("Unmatched SEXPTYPE!");
            return overloaded_impl<fallthrough>(fallthrough());
        }
    }
    return -1; // not reached
}

/*** R

overloaded("a string")
#[1] 8

overloaded(10L)
#[1] 20

overloaded(10)
#[1] 20

overloaded(TRUE)
#[1] -1
#Warning message:
#In overloaded(TRUE) : Unmatched SEXPTYPE!

overloaded(2 + 2i)
#[1] -1
#Warning message:
#In overloaded(2 + (0+2i)) : Unmatched SEXPTYPE!

*/ 


案例:REALSXP 就在那儿,因为R默认为数字而不是 integer ,例如没有它,您将拥有:


The case: REALSXP is just there because R defaults to numeric instead of integer, e.g. without it you would have:

overloaded(10)
#[1] -1
#Warning message:
#In overloaded(10) : Unmatched SEXPTYPE! 


此策略的一种变体是创建一个包含变体对象的包装器类,其中将基于 switch 的类型推导逻辑移入构造函数中,并通过应用访问者进行方法分派图案.对于上面的简单示例来说,这并没有真正的道理,但是在某些情况下,您可能需要在对象上调用多个不同的函数,因此可以避免很多代码重复,例如 switch(TYPEOF(x)){...} 块不必在每个函数中都存在.这是一个例子

BH 软件包提供了Boost C ++库,从而实现了更大的规模.


A variation of this strategy is to create a wrapper class housing a variant object, wherein the switch-based type deduction logic is moved into the constructor, and method dispatch is carried out by applying the visitor pattern. This is not really justified for the simple example above, but in situations where you have several different functions that may be called on the object, it can save you from a lot of code duplication, as the switch(TYPEOF(x)) {...} block does not need to exist in each function. Here's an example where I've done this on a larger scale with the Boost C++ libraries, courtesy of the BH package.

无论如何,我们可以使用变体/访问者技术来重写原始示例,如下所示:

At any rate, we could rewrite the original example using the variant / visitor technique as follows:

// [[Rcpp::depends(BH)]]
#include <Rcpp.h>
#include <boost/variant.hpp>

class variant {
private:
    struct fallthrough {};
    typedef boost::variant<
        int,
        std::string,
        fallthrough
    > variant_t;

    variant_t v;

    struct overloaded_visitor : public boost::static_visitor<int> {
        int operator()(const std::string& x) const {
            return x.size();
        }

        int operator()(const int& x) const {
            return x * 2;
        }

        template <typename T>
        int operator()(const T&) const {
            return -1;
        } 
    };

public:
    variant(SEXP x) 
    {
        switch (TYPEOF(x)) {
            case INTSXP: {
                v = variant_t(INTEGER(x)[0]);
                break;
            }
            case REALSXP: {
                v = variant_t((int)(REAL(x)[0]));
                break;
            }
            case STRSXP: {
                std::string tmp = CHAR(STRING_ELT(x, 0));
                v = variant_t(tmp);
                break;
            }
            default: {
                Rcpp::warning("Unmatched SEXPTYPE!");
                v = variant_t(fallthrough());
                break;
            }
        }
    }

    int overloaded() const {
        return boost::apply_visitor(overloaded_visitor(), v);
    }
};


// [[Rcpp::export]]
int overloaded(SEXP x) {
    return variant(x).overloaded();
}

/*** R

overloaded("a string")
#[1] 8

overloaded(10L)
#[1] 20

overloaded(12)
#[1] 24

overloaded(FALSE)
#[1] -1
#Warning messages:
#In overloaded(FALSE) : Unmatched SEXPTYPE!

overloaded(2 + 2i)
#[1] -1
#Warning messages:
#In overloaded(2 + (0+2i)) : Unmatched SEXPTYPE!

*/

这篇关于创建两个具有相同名称但参数类型不同的R函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆