在R中執行此操作的一般機制是通過R_RegisterCCallable
和R_GetCCallable
使函數指針可用。一個例子見R-exts
。
這意味着符號是根據需要動態解析的 - 您實際上並不需要「鏈接」到其他程序包本身;您只需要標題,以便在代碼執行後可以在日後正確解析符號。請注意,LinkingTo:
字段實際上是一個用詞不當 - 它只是給你的標題,它實際上並不是鏈接你到(爲該庫生成的)包。
幸運的是,這可以與Rcpp::interfaces
屬性,在RcppExports.cpp
基本上自動生成R_RegisterCCallable
入口點,並提供所產生的報頭文件中使用R_GetCCallable
包裝函數實現自動化。
例如,假設我有一個傻包稱爲RcppInterfaces
,含有這種在src/test.cpp
(用Includes:
和LinkingTo:
具有Rcpp
DESCRIPTION
)。請注意0註釋,這表示Rcpp
該文件應該同時獲得R導出和C++頭導出。
// [[Rcpp::interfaces(r, cpp)]]
#include <Rcpp.h>
// [[Rcpp::export]]
void hello() {
Rcpp::Rcout << "Hello!\n";
}
如果我打電話Rcpp::compileAttributes()
,你會看到寫到RcppExports.cpp
以下「東西」:
// This file was generated by Rcpp::compileAttributes
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393
#include <Rcpp.h>
#include <string>
#include <set>
using namespace Rcpp;
// hello
void hello();
static SEXP RcppInterfaces_hello_try() {
BEGIN_RCPP
{
hello();
}
return R_NilValue;
END_RCPP_RETURN_ERROR
}
RcppExport SEXP RcppInterfaces_hello() {
SEXP __result;
{
Rcpp::RNGScope __rngScope;
__result = PROTECT(RcppInterfaces_hello_try());
}
Rboolean __isInterrupt = Rf_inherits(__result, "interrupted-error");
if (__isInterrupt) {
UNPROTECT(1);
Rf_onintr();
}
Rboolean __isError = Rf_inherits(__result, "try-error");
if (__isError) {
SEXP __msgSEXP = Rf_asChar(__result);
UNPROTECT(1);
Rf_error(CHAR(__msgSEXP));
}
UNPROTECT(1);
return __result;
}
// validate (ensure exported C++ functions exist before calling them)
static int RcppInterfaces_RcppExport_validate(const char* sig) {
static std::set<std::string> signatures;
if (signatures.empty()) {
signatures.insert("void(*hello)()");
}
return signatures.find(sig) != signatures.end();
}
// registerCCallable (register entry points for exported C++ functions)
RcppExport SEXP RcppInterfaces_RcppExport_registerCCallable() {
R_RegisterCCallable("RcppInterfaces", "RcppInterfaces_hello", (DL_FUNC)RcppInterfaces_hello_try);
R_RegisterCCallable("RcppInterfaces", "RcppInterfaces_RcppExport_validate", (DL_FUNC)RcppInterfaces_RcppExport_validate);
return R_NilValue;
}
注意,最早期的東西是樣板,確保的異常安全版本該函數被調用;最後你基本上有了爲其他軟件包註冊可調用函數的機制。在inst/include/RcppInterfaces_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_GetCCallable
呼叫,允許其他包的作者爲「只使用」該功能,與R_GetCCallable
東西,內聯和直接在函數調用中進行管理(使用靜態指針,必要時可以填充一次)。
所以,就因爲這RcppInterfaces
包的用戶而言,他們可以叫
RcppInterfaces::hello()
在他們的代碼,我們只是自動確保函數指針擡起頭來,用(安全!)在運行時,使用R自己的機制。
這裏有一些信息在http://r-pkgs.had.co.nz/src.html – hadley 2014-11-24 21:39:50