Create two R functions with same name but different type of argument

柔情痞子 提交于 2020-01-13 08:37:06

问题


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?

I have the following functions:

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
}

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

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

回答1:


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!

*/ 

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! 

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!

*/



回答2:


The RcppExport is a simple
#define RcppExport extern "C"
Hence, myFunction has a C-like naming convention. Therefore, it can not be overloaded, because you need C++ style of names for this case.



来源:https://stackoverflow.com/questions/37023726/create-two-r-functions-with-same-name-but-different-type-of-argument

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!