Создать две функции с одинаковым именем, но разного типа аргумент
Я создаю пакет с 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++.