2016-05-04 101 views
4

我正在創建一個包含Rcpp的包,並且我想要一個功能可以接受爲參數std::stringint。我該怎麼做?創建兩個具有相同名稱但參數類型不同的R函數

我有以下功能:

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

回答

5

正如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只是那裏,因爲ř默認爲numeric代替integer,例如沒有它,你將有:

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

這種策略的一種變化是創建一個包裝類殼體的變型的對象,其中,所述基於switch類型推導邏輯被移動到構造函數和方法分派是通過應用訪客模式進行。對於上面這個簡單的例子來說,這並不合理,但是在有幾個不同的函數可以在對象上調用的情況下,它可以爲您節省大量的代碼重複,因爲switch(TYPEOF(x)) {...}塊不需要存在於每個塊中功能。 Here's an example我已經用Boost C++庫在更大的範圍內完成了這個工作,該工具包由BH提供。

無論如何,我們可以使用株/訪問者技術如下改寫原來的例子:

// [[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++風格的名稱。

+0

那麼,是不是可以有它可以接受不同的數據類型的功能? –

+0

C不支持函數重載 – blackmesa

+0

好吧,有一個獨特的方法,我可以做一種typeof來確定,如果輸入是一個數字或字符串,並根據重定向到一個c + +函數或其他? –

相關問題