创建两个具有相同名称但不同类型的参数的R函数

时间:2016-05-04 09:29:39

标签: c++ r rcpp

我正在使用Rcpp创建一个包,我希望有一个函数,而不是std::stringint作为参数。我该怎么办?

我有以下功能:

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

在我定义R函数的文件中:

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

当我构建我的包时,我收到以下错误:

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) {

2 个答案:

答案 0 :(得分:7)

正如Dirk在评论中指出的那样,这可以通过从(单个)导出函数中调度适当的实现函数来完成。典型方法涉及switch语句和TYPEOF宏,如下所示:

#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!

*/ 

case: REALSXP就在那里,因为R默认为numeric而不是integer,例如没有它你会有:

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

此策略的一种变体是创建一个包含变体对象的包装类,其中基于switch的类型推导逻辑被移动到构造函数中,并且通过应用访问者模式来执行方法调度。这对于上面的简单示例来说并不合理,但是在您可以在对象上调用多个不同函数的情况下,它可以避免大量代码重复,因为switch(TYPEOF(x)) {...}块不需要存在于每个功能中。 Here's an example我使用Boost C ++库在更大范围内完成了这项工作,由BH包提供。

无论如何,我们可以使用variant / visitor技术重写原始示例,如下所示:

// [[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!

*/

答案 1 :(得分:2)

RcppExport是一个简单的#define RcppExport extern "C"因此,myFunction具有类似C的命名约定。因此,它不能重载,因为在这种情况下你需要C ++样式的名称。