gpt4 book ai didi

c++ - 具有Rcpp::interfaces的C++接口(interface)不适用于返回std::pair的函数

转载 作者:行者123 更新时间:2023-11-30 01:08:35 26 4
gpt4 key购买 nike

我想为包中的函数提供C++接口(interface),该包使用std::pair返回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);
}

这是一个错误还是出了什么问题?

最佳答案

However, the compiler throws a shitload of errors, starting with:

.../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.h wrap.h 的任何错误)是由使用 // [[Rcpp::export]]属性触发的,该属性(尝试)生成所需的样板代码来打开将 std::pair<int, int>转换成R知道如何处理的东西(即某种 SEXP)。

根据您的评论

But I don't want to return it back to R at all [...]



您处在良好的状态,因为这意味着您不必麻烦编写处理 std::pair<int, int>的转换器函数-只需删除 // [[Rcpp::export]]声明,即可解决上述错误消息。

关于问题的实质,

I simply want to use some C++ functions in another package



我可以为您提供两种方法,顺便说一句,它们都不使用 // [[Rcpp::interfaces]]属性。

简单的方法

我假设您提供的示例是实际用例的简化,但是如果可能, 会竭尽所能提供纯标题的接口(interface)。虽然这种方法有潜在的缺点(例如,讨论过的 in this question),但是它将大大简化您要尝试执行的操作,而IMO远远超过了额外花费几分钟的编译时间。如果您打算提供严格的模板类和/或函数的接口(interface),那么生活就很好了,因为无论如何这是您唯一的选择。

为了演示,请考虑以下接口(interface)包的目录结构:
# 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包中,它将从 bla包中调用 hinterface,事情甚至更加简单:
# nathan@nathan-deb:/tmp$ tree hclient/
# hclient/
# ├── DESCRIPTION
# ├── NAMESPACE
# ├── R
# └── src
# ├── hclient.cpp

用户需要做的所有事情(假设软件包是通过R通过 Rcpp::Rcpp.package.skeleton从R生成的),将 hinterface添加到 LinkingTo文件中的 DESCRIPTION字段中,
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)(因此客户端包中的代码将需要链接到实现),因此您将需要跳过所有步骤来安抚链接器,这从来都不是一件有趣的事情时间。此外,尽管您可以在某种程度上减轻此负担,但它会比以前增加更多的负担,如稍后所示。

事不宜迟, interface的布局如下:
# nathan@nathan-deb:/tmp$ tree interface/
# interface/
# ├── DESCRIPTION
# ├── inst
# │   └── include
# │   └── interface.h
# ├── NAMESPACE
# ├── R
# │   └── libpath.R
# └── src
# ├── bla.cpp
# ├── Makevars
# └── Makevars.win

由于我们不再在 bla*.hpp文件中实现 *.h,因此接口(interface)头 interface.h将仅包含函数原型(prototype):

#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(),并且通过链接到我并不意味着仅将 interface添加到 LinkingTo文件中的 DESCRIPTION字段中,具有讽刺意味的是与之无关编译中的链接阶段。未能做到这一点-例如仅包含 header 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++接口(interface),则应导出该函数并适当地记录下来,以便他们了解如何使用它。它返回的路径将完全取决于(必要时)从其调用的特定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

关于c++ - 具有Rcpp::interfaces的C++接口(interface)不适用于返回std::pair的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42261822/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com