Создать две функции с одинаковым именем, но разного типа аргумент

Я создаю пакет с Rcpp, и я хочу иметь функцию, чем могу принять в качестве аргумента a std::string или int. Как я могу это сделать?

у меня есть следующие функции:

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 ответов


как отметил Дирк в комментариях, Это можно сделать, отправив соответствующую функцию реализации из (одной) экспортируемой функции. Типичный подход включает в себя 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)) {...} блок не должен существовать в каждой функции. вот пример где я это сделал более крупный масштаб с библиотеками 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!

*/

RcppExport является простым
#define RcppExport extern "C"
следовательно, myFunction имеет c-подобное соглашение об именах. Поэтому его нельзя перегружать, так как для этого случая нужен стиль имен c++.