C++ 如何共享C++;R包之间基于Rcpp的库中的函数?
我正在Rcpp中开发一个简单的库,用于构建哈夫曼树。它有一个工作的R接口,我可以从其他包调用,但是我也希望直接调用C++函数,在我开发的其他基于RCPP的包中,用C++代码调用。 我已经知道了如何将第一个包的头放在C++ 如何共享C++;R包之间基于Rcpp的库中的函数?,c++,r,rcpp,C++,R,Rcpp,我正在Rcpp中开发一个简单的库,用于构建哈夫曼树。它有一个工作的R接口,我可以从其他包调用,但是我也希望直接调用C++函数,在我开发的其他基于RCPP的包中,用C++代码调用。 我已经知道了如何将第一个包的头放在inst/include目录中,以便它在第二个包中可用。但是,当在第二个包的命名空间< /C>文件中调用 USEDYLYB时,加载它调用第一个包中的函数的C++代码,对于我正在使用的函数,我得到了一个未定义的符号错误。我将第一个包列在第二个包的说明文件中的导入,依赖于,以及链接到 这是
inst/include
目录中,以便它在第二个包中可用。但是,当在第二个包的说明
文件中的导入
,依赖于
,以及链接到
这是我第一次尝试使用任何非基于R的包,我通过Rstudio的“Build&Reload”命令进行所有开发,并在创建包以生成初始目录结构时使用了“Package w/Rcpp”选项。是的,链接步骤比较困难,但仍然可行
例如,查看RcppXts包如何导入xts包导出的符号。这一切都相当乏味
我认为Kevin在他的Kmisc软件包中有一些必要注册步骤的助手。我一直想阅读这些内容,但还不需要它们/还没有时间。在R中这样做的一般机制是通过
R\u registercallable
和R\u GetCCallable
使函数指针可用。有关示例,请参见
这意味着符号是根据需要动态解析的——实际上不需要“链接”到另一个包本身;您只需要标题,以便稍后在执行代码时可以正确解析符号。请注意,LinkingTo:
字段实际上是一个用词不当的字段——它只提供标题,实际上并没有将您链接到(为该)包生成的库
谢天谢地,这可以通过属性实现自动化,该属性本质上自动生成RcppExports.cpp
中的R\u registercallable
入口点,并在生成的头文件中使用R\u GetCCallable
提供包装函数
例如,假设我有一个名为rcppinertfaces
的愚蠢包,它在src/test.cpp
中包含这个包(带有说明在中包含Rcpp
包括:
和链接到:
)。请注意<代码> /[[RCPP::接口(r,CPP)] < /COD>注释,该信号向<代码> Rcpp <代码> >该文件应同时得到R导出和C++头输出。< /P>
// [[Rcpp::interfaces(r, cpp)]]
#include <Rcpp.h>
// [[Rcpp::export]]
void hello() {
Rcpp::Rcout << "Hello!\n";
}
注意,早期的大部分内容都是样板文件,可以确保函数的异常安全版本是可调用的;最后,您基本上拥有了为其他包注册可调用函数的机制。在inst/include/rcppineterfaces\u RcppExports.h
中,我们有:
// This file was generated by Rcpp::compileAttributes
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
#ifndef __RcppInterfaces_RcppExports_h__
#define __RcppInterfaces_RcppExports_h__
#include <Rcpp.h>
namespace RcppInterfaces {
using namespace Rcpp;
namespace {
void validateSignature(const char* sig) {
Rcpp::Function require = Rcpp::Environment::base_env()["require"];
require("RcppInterfaces", Rcpp::Named("quietly") = true);
typedef int(*Ptr_validate)(const char*);
static Ptr_validate p_validate = (Ptr_validate)
R_GetCCallable("RcppInterfaces", "RcppInterfaces_RcppExport_validate");
if (!p_validate(sig)) {
throw Rcpp::function_not_exported(
"C++ function with signature '" + std::string(sig) + "' not found in RcppInterfaces");
}
}
}
inline void hello() {
typedef SEXP(*Ptr_hello)();
static Ptr_hello p_hello = NULL;
if (p_hello == NULL) {
validateSignature("void(*hello)()");
p_hello = (Ptr_hello)R_GetCCallable("RcppInterfaces", "RcppInterfaces_hello");
}
RObject __result;
{
RNGScope __rngScope;
__result = p_hello();
}
if (__result.inherits("interrupted-error"))
throw Rcpp::internal::InterruptedException();
if (__result.inherits("try-error"))
throw Rcpp::exception(as<std::string>(__result).c_str());
return Rcpp::as<void >(__result);
}
}
#endif // __RcppInterfaces_RcppExports_h__
在他们的代码中,我们只是使用R自己的机制自动确保在运行时查找和使用函数指针(安全!)。Kevin对R\u RegisterCallable
和R\u GetCCallable
的精彩描述。就我个人而言,我认为应该将所有可供您的包或其他包使用的代码都放在头文件中。这就不那么脆弱了 谢谢你的指点;当我把它全部整理出来时,我会列出一个简短的例子。另外,感谢Rcpp:在这种情况下,Kmisc
只是自动注册“本机例程”,例如。但我最近在Hadley那里发现,仅仅在名称空间中编写useDynLib(,)
实际上是等效的……可移植的?这将是一个巨大的帮助。我不知道C级函数注册,所以我只使用命名空间中的useDynLib()
完成了注册。似乎在跨平台工作,并受到Cran的祝福。在
// This file was generated by Rcpp::compileAttributes
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
#ifndef __RcppInterfaces_RcppExports_h__
#define __RcppInterfaces_RcppExports_h__
#include <Rcpp.h>
namespace RcppInterfaces {
using namespace Rcpp;
namespace {
void validateSignature(const char* sig) {
Rcpp::Function require = Rcpp::Environment::base_env()["require"];
require("RcppInterfaces", Rcpp::Named("quietly") = true);
typedef int(*Ptr_validate)(const char*);
static Ptr_validate p_validate = (Ptr_validate)
R_GetCCallable("RcppInterfaces", "RcppInterfaces_RcppExport_validate");
if (!p_validate(sig)) {
throw Rcpp::function_not_exported(
"C++ function with signature '" + std::string(sig) + "' not found in RcppInterfaces");
}
}
}
inline void hello() {
typedef SEXP(*Ptr_hello)();
static Ptr_hello p_hello = NULL;
if (p_hello == NULL) {
validateSignature("void(*hello)()");
p_hello = (Ptr_hello)R_GetCCallable("RcppInterfaces", "RcppInterfaces_hello");
}
RObject __result;
{
RNGScope __rngScope;
__result = p_hello();
}
if (__result.inherits("interrupted-error"))
throw Rcpp::internal::InterruptedException();
if (__result.inherits("try-error"))
throw Rcpp::exception(as<std::string>(__result).c_str());
return Rcpp::as<void >(__result);
}
}
#endif // __RcppInterfaces_RcppExports_h__
RcppInterfaces::hello()