![](/img/trans.png)
[英]Is there a standard C++ function object for taking apart a 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.h或wrap.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/
目录中,创建一个Makevars
和Makevars.win
,每个包含
PKG_CPPFLAGS = -I../inst/include
以及您可能需要设置的任何其他必要的编译器选项。 最后,我添加了一个虚拟源文件以使程序包能够生成,但是如果您实际上是在导出一个或多个C ++函数,则没有必要:
#include "hinterface.h"
void noop() { return; }
在hclient
包中( hclient
从hinterface
包中调用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
和以前一样, Makevars
和Makevars.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
软件包中的源文件没有什么不同。 有趣的地方是Makevars
和Makevars.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.