簡體   English   中英

具有Rcpp :: interfaces的C ++接口不適用於返回std :: pair的函數

[英]C++ interface with Rcpp::interfaces not working for a function returning std::pair

我想為使用Rcpp::interface返回std::pair的包中的函數提供一個C ++ Rcpp::interface 但是,編譯器會引發大量錯誤,從以下內容開始:

.../Rcpp/include/Rcpp/internal/Exporter.h:31:31: error: no matching
function for call to ‘std::pair<int, int>::pair(SEXPREC*&)’
   Exporter( SEXP x ) : t(x){}

這是一個簡單的示例:

#include <Rcpp.h>
#include <utility>

// [[Rcpp::interfaces(cpp)]]

// [[Rcpp::export]]
std::pair<int, int> bla()
{
  return std::make_pair(1,1);
}

此示例函數生成的代碼如下:

inline std::pair<int, int> bla() {
    typedef SEXP(*Ptr_bla)();
    static Ptr_bla p_bla = NULL;
    if (p_bla == NULL) {
        validateSignature("std::pair<int, int>(*bla)()");
        p_bla = (Ptr_bla)R_GetCCallable("testinclude", "testinclude_bla");
    }
    RObject rcpp_result_gen;
    {
        RNGScope RCPP_rngScope_gen;
        rcpp_result_gen = p_bla();
    }
    if (rcpp_result_gen.inherits("interrupted-error"))
        throw Rcpp::internal::InterruptedException();
    if (rcpp_result_gen.inherits("try-error"))
        throw Rcpp::exception(as<std::string>(rcpp_result_gen).c_str());
    return Rcpp::as<std::pair<int, int> >(rcpp_result_gen);
}

這是一個錯誤還是出了什么問題?

但是,編譯器會引發大量錯誤,從以下內容開始:

 .../Rcpp/include/Rcpp/internal/Exporter.h:31:31: error: no matching function for call to 'std::pair<int, int>::pair(SEXPREC*&)' Exporter( SEXP x ) : t(x){} 

如Dirk所指出的,此錯誤(以及通常引用Exporter.hwrap.h的任何錯誤)是通過使用// [[Rcpp::export]]屬性觸發的(嘗試生成適當的樣板)將std::pair<int, int>轉換成R知道如何處理的代碼(即某種類型的SEXP )。

根據您的評論

但是我根本不想將其返回給R [...]

您處在良好的狀態,因為這意味着您不必麻煩編寫處理std::pair<int, int>的轉換器函數-只需刪除// [[Rcpp::export]]聲明,它將處理上述錯誤消息。


關於問題的實質,

我只是想在另一個包中使用一些C ++函數

我可以為您提供兩種方法,順便說一句, 它們都不使用// [[Rcpp::interfaces]]屬性。


簡單的方法

我假設您提供的示例是您實際使用情況的簡化,但是,如果有可能, 請盡一切可能提供僅標頭的接口 盡管這種方法有潛在的弊端(例如, 在本問題中進行了討論),但它將大大簡化您要嘗試執行的操作,而IMO,這遠遠超過了額外幾分鍾的編譯時間。 如果您打算提供嚴格的模板類和/或函數的接口,那么生活就很好了,因為無論如何這是您唯一的選擇。

為了演示,請考慮以下接口包的目錄結構:

# nathan@nathan-deb:/tmp$ tree hinterface/
# hinterface/
# ├── DESCRIPTION
# ├── inst
# │   └── include
# │       ├── hinterface
# │       │   └── hinterface.hpp
# │       └── hinterface.h
# ├── NAMESPACE
# ├── R
# └── src
#     ├── hinterface.cpp
#     ├── Makevars
#     └── Makevars.win

首先創建目錄inst/inst/include/ ,因為這會導致R在將包安裝在用戶計算機上時將頭文件hinterface.h復制到hinterface庫目錄中。 此外,我創建了inst/include/hinterface/hinterface.hpp ,其中包含實現:

#ifndef hinterface__hinterface__hpp
#define hinterface__hinterface__hpp

#include <Rcpp.h>
#include <utility>

namespace hinterface {

inline std::pair<int, int> bla()
{ return std::make_pair(1, 1); }

} // hinterface

#endif // hinterface__hinterface__hpp

這不是嚴格必要的,但是遵循這是一個合理的約定,尤其是在您有許多頭文件的情況下。 返回上一級, hinterface.h文件(客戶端實際上將在其源代碼中包括)包含以下內容:

#ifndef hinterface__hinterface__h
#define hinterface__hinterface__h

#include "hinterface/hinterface.hpp"
// possibly other
// header files
// to include

#endif // hinterface__hinterface__h

src/目錄中,創建一個MakevarsMakevars.win ,每個包含

PKG_CPPFLAGS = -I../inst/include

以及您可能需要設置的任何其他必要的編譯器選項。 最后,我添加了一個虛擬源文件以使程序包能夠生成,但是如果您實際上是在導出一個或多個C ++函數,則沒有必要:

#include "hinterface.h"

void noop() { return; }

hclient包中( hclienthinterface包中調用bla ,事情變得更加簡單:

# nathan@nathan-deb:/tmp$ tree hclient/
# hclient/
# ├── DESCRIPTION
# ├── NAMESPACE
# ├── R
# └── src
#     ├── hclient.cpp

用戶需要做的所有事情(假設軟件包是通過Rcpp::Rcpp.package.skeleton從R生成的),將hinterface添加到DESCRIPTION文件中的LinkingTo字段中,

LinkingTo: Rcpp,
    hinterface

在其源文件中添加對// [[Rcpp::depends(hinterface)]]屬性的調用,並包括hinterface.h

// hclient.cpp
// [[Rcpp::depends(hinterface)]]
#include <Rcpp.h>
#include <hinterface.h>

// [[Rcpp::export]]
void call_bla()
{
    std::pair<int, int> x = hinterface::bla();
    std::printf(
        "x.first = %d\nx.second = %d\n",
        x.first, x.second
    );
}

構建此程序包,我們可以通過從R調用它來看到它按預期工作:

hclient::call_bla()
# x.first = 1
# x.second = 1

艱難的道路

在這種方法中,由於您將只在頭文件中提供一個接口(因此,客戶端程序包中的代碼將需要鏈接到實現),因此您將需要跳個圈來安撫鏈接器,這從來都不是一件有趣的事情時間。 此外,盡管您可以在某種程度上減輕此負擔,但它會比以前增加更多的負擔,如稍后所示。

事不宜遲, interface的布局如下:

# nathan@nathan-deb:/tmp$ tree interface/
# interface/
# ├── DESCRIPTION
# ├── inst
# │   └── include
# │       └── interface.h
# ├── NAMESPACE
# ├── R
# │   └── libpath.R
# └── src
#     ├── bla.cpp
#     ├── Makevars
#     └── Makevars.win

由於我們不再在*.hpp*.h文件中實現bla ,因此接口頭interface.h只包含一個函數原型:

#ifndef interface__interface__h
#define interface__interface__h

#include <Rcpp.h>
#include <utility>

namespace interface {

std::pair<int, int> bla();

} // interface

#endif // interface__interface__h

和以前一樣, MakevarsMakevars.win僅包含PKG_CPPFLAGS = -I../inst/include (以及其他可能需要設置的標志)。 bla.cpp非常簡單,僅包含適當的實現:

#include "interface.h"

namespace interface {

std::pair<int, int> bla()
{ return std::make_pair(1, 1); }

} // interface

如所暗示的那樣,客戶端程序包將需要將其代碼鏈接至interface才能真正使用 bla() ,並且通過鏈接至我並不是說DESCRIPTION文件中的LinkingTo字段添加interface ,具有諷刺意味的是,在編譯中執行鏈接階段。 否則,例如包含標頭interface.h ,將導致R CMD INSTALL暫停,因為它將在嘗試加載客戶端程序包時找不到合適的符號。 對於用戶來說,這可能是一個非常令人沮喪的錯誤。 幸運的是,您可以通過提供類似於以下功能的東西來簡化事情,該函數生成interface共享庫的位置:

# libpath.R
.libpath <- function() {
    cat(sprintf(
        "%s/interface/libs/interface%s",
        installed.packages()["interface","LibPath"][1],
        .Platform$dynlib.ext
    ))
}

我以開頭的名字. 這樣就不會導出(默認情況下),因此不需要進行記錄。 如果您打算讓人們在自己的程序包中使用C ++接口,則應導出該函數並適當地記錄下來,以便他們了解如何使用它。 它返回的路徑將完全取決於(必要時)從其調用的特定R安裝,但是在我的Linux機器上,它看起來像這樣:

interface:::.libpath()
# /home/nathan/R/x86_64-pc-linux-gnu-library/3.4/interface/libs/interface.so

轉到適當命名的client程序包,

# nathan@nathan-deb:/tmp$ tree client/
# client/
# ├── DESCRIPTION
# ├── NAMESPACE
# ├── R
# └── src
#     ├── client.cpp
#     ├── Makevars
#     ├── Makevars.win

再次需要DESCRIPTION文件

LinkingTo: Rcpp,
        interface

以便正確定位頭文件。 源文件client.cpp如下所示:

// [[Rcpp::depends(interface)]]
#include <Rcpp.h>
#include <interface.h>

// [[Rcpp::export]]
void call_bla()
{
    std::pair<int, int> x = interface::bla();
    std::printf(
        "x.first = %d\nx.second = %d\n",
        x.first, x.second
    );
}

這與hclient軟件包中的源文件沒有什么不同。 有趣的地方是MakevarsMakevars.win ,現在包含

PKG_LIBS += `${R_HOME}/bin/Rscript -e "cat(interface:::.libpath())"`

在這里,我們使用在interface包中定義的幫助器功能來確保鏈接器可以使用適當的符號。 構建它並從R中進行測試,

client::call_bla()
# x.first = 1
# x.second = 1

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM