从c++调用R函数
Calling R Function from C++
我想,在我自己编译的c++代码中,检查是否在R中加载了一个库包(如果没有,加载它),从该库中调用函数并将结果返回到我的c++代码中。
谁能给我指个方向吗?似乎有太多关于R的信息,以及从c++调用R的不同方法,反之亦然,但我还没有确切地遇到我想要做的。谢谢。
德克可能是对的,RInside让生活更容易。但对于顽固派来说……其精髓来自编写R扩展第8.1节和8.2节,以及随R分发的示例。下面的材料包括构造和计算调用;处理返回值是另一个主题(在某种意义上更容易)。
置>让我们假设Linux/Mac平台。首先,R必须经过编译,以允许链接到共享或静态R库。我使用R源代码的svn副本,在目录~/src/R-devel
中。切换到另一个目录,命名为~/bin/R-devel
,然后是
~/src/R-devel/configure --enable-R-shlib
make -j
生成~/bin/R-devel/lib/libR.so
;也许你使用的发行版已经有这个功能了?-j
标志并行运行,这大大加快了构建速度。
嵌入的例子在~/src/R-devel/tests/Embedding
中,它们可以用cd ~/bin/R-devel/tests/Embedding && make
制作。显然,这些示例的源代码非常有指导意义。
为了说明,创建一个文件embed.cpp
。首先包括定义R数据结构的头文件和R嵌入接口;它们位于bin/R-devel/include
中,并作为主要文档。我们也有一个函数的原型,它将完成所有的工作
#include <Rembedded.h>
#include <Rdefines.h>
static void doSplinesExample();
工作流程是开始R,完成工作,结束R:
int
main(int argc, char *argv[])
{
Rf_initEmbeddedR(argc, argv);
doSplinesExample();
Rf_endEmbeddedR(0);
return 0;
}
Embedding
下的示例包括调用library(splines)
,设置命名选项,然后运行函数example("ns")
的示例。下面是执行此操作的例程
static void
doSplinesExample()
{
SEXP e, result;
int errorOccurred;
// create and evaluate 'library(splines)'
PROTECT(e = lang2(install("library"), mkString("splines")));
R_tryEval(e, R_GlobalEnv, &errorOccurred);
if (errorOccurred) {
// handle error
}
UNPROTECT(1);
// 'options(FALSE)' ...
PROTECT(e = lang2(install("options"), ScalarLogical(0)));
// ... modified to 'options(example.ask=FALSE)' (this is obscure)
SET_TAG(CDR(e), install("example.ask"));
R_tryEval(e, R_GlobalEnv, NULL);
UNPROTECT(1);
// 'example("ns")'
PROTECT(e = lang2(install("example"), mkString("ns")));
R_tryEval(e, R_GlobalEnv, &errorOccurred);
UNPROTECT(1);
}
编译并运行
我们现在已经准备好把所有东西放在一起了。编译器需要知道头文件和库文件在哪里
g++ -I/home/user/bin/R-devel/include -L/home/user/bin/R-devel/lib -lR embed.cpp
编译后的应用程序需要在正确的环境中运行,例如,正确设置R_HOME;使用
可以很容易地安排这一点(显然,已部署的应用程序希望采用更广泛的方法)。R CMD ./a.out
根据你的目标,编写R扩展第8节中的一些部分是不相关的,例如,在R之上实现GUI需要回调,但不需要计算简单的代码块。
一些细节详细介绍一下…SEXP (s表达式)是R表示基本类型(整数、逻辑、语言调用等)的基础数据结构。行
PROTECT(e = lang2(install("library"), mkString("splines")));
创建一个符号library
和一个字符串"splines"
,并将它们放入一个由两个元素组成的语言结构中。这将构造一个未求值的语言对象,大约相当于R中的quote(library("splines"))
。lang2
返回一个从R的内存池中分配的SEXP,并且需要从垃圾收集中提取PROTECT
。PROTECT
将e
指向的地址添加到保护堆栈中,当内存不再需要保护时,该地址从堆栈中弹出(UNPROTECT(1)
,向下几行)。行
R_tryEval(e, R_GlobalEnv, &errorOccurred);
尝试在R的全局环境中评估e
。如果发生错误,则将errorOccurred
设置为非0。R_tryEval
返回一个表示函数结果的SEXP,但我们在这里忽略它。因为我们不再需要分配给library("splines")
的内存,所以我们告诉R它不再被保护。
下一段代码与此类似,对options(example.ask=FALSE)
求值,但调用的构造更复杂。lang2
创建的s表达式是一个对列表,概念上有一个节点、一个左指针(CAR)和一个右指针(CDR)。e
的左指针指向符号options
。e
的右指针指向pair列表中的另一个节点,该节点的左指针为FALSE
(右指针为R_NilValue
,表示语言表达式结束)。pair列表中的每个节点都可以有一个TAG
,其含义取决于节点所扮演的角色。这里我们附加了一个参数名。
SET_TAG(CDR(e), install("example.ask"));
下一行计算我们构造的表达式(options(example.ask=FALSE)
),使用NULL
表示我们将忽略函数求值的成功或失败。R-devel/tests/Embedding/RParseEval.c
中说明了构造和求值该调用的另一种方法,此处改成
PROTECT(tmp = mkString("options(example.ask=FALSE)"));
PROTECT(e = R_ParseVector(tmp, 1, &status, R_NilValue));
R_tryEval(VECTOR_ELT(e, 0), R_GlobalEnv, NULL);
UNPROTECT(2);
,但这似乎不是一个好的策略,因为它混合了R和C代码,并且不允许在R函数中使用计算参数。相反,在R中编写和管理R代码(例如,创建一个包含执行一系列复杂R操作的函数的包),您的C代码使用。
上面的最后一个代码块构造并计算example("ns")
。Rf_tryEval
返回函数调用的结果,所以
SEXP result;
PROTECT(result = Rf_tryEval(e, R_GlobalEnv, &errorOccurred));
// ...
UNPROTECT(1);
将捕获它以供后续处理。
Rcpp允许您轻松地用c++代码扩展R,并且还可以将c++代码调用回R。包中包含的示例显示了这一点。
但也许你真正想要的是保留你的c++程序(即你拥有main()
)并调用R?这是最容易做到的RInside允许您非常容易地将R嵌入到您的c++应用程序中,并且库的测试,如果需要的话加载和函数调用都非常容易做到,并且(超过一打)包含的示例向您展示了如何。Rcpp仍然可以帮助您来回获得结果。
Edit:由于Martin好心地以官方方式展示了,我忍不住将其与RInside附带的示例之一进行了对比。这是我曾经快速写的东西,以帮助那些在r-help上询问如何加载(投资组合优化)库并使用它的人。它满足您的要求:加载一个库,访问一些数据,将权重向量从c++传递到R,部署R并获得结果。
// -*- mode: C++; c-indent-level: 4; c-basic-offset: 4; tab-width: 8; -*-
//
// Simple example for the repeated r-devel mails by Abhijit Bera
//
// Copyright (C) 2009 Dirk Eddelbuettel
// Copyright (C) 2010 - 2011 Dirk Eddelbuettel and Romain Francois
#include <RInside.h> // for the embedded R via RInside
int main(int argc, char *argv[]) {
try {
RInside R(argc, argv); // create an embedded R instance
std::string txt = "suppressMessages(library(fPortfolio))";
R.parseEvalQ(txt); // load library, no return value
txt = "M <- as.matrix(SWX.RET); print(head(M)); M";
// assign mat. M to NumericMatrix
Rcpp::NumericMatrix M = R.parseEval(txt);
std::cout << "M has "
<< M.nrow() << " rows and "
<< M.ncol() << " cols" << std::endl;
txt = "colnames(M)"; // assign columns names of M to ans and
// into string vector cnames
Rcpp::CharacterVector cnames = R.parseEval(txt);
for (int i=0; i<M.ncol(); i++) {
std::cout << "Column " << cnames[i]
<< " in row 42 has " << M(42,i) << std::endl;
}
} catch(std::exception& ex) {
std::cerr << "Exception caught: " << ex.what() << std::endl;
} catch(...) {
std::cerr << "Unknown exception caught" << std::endl;
}
exit(0);
}
这个rinside_sample2.cpp
,包中还有更多的例子。要构建它,您只需说'make rinside_sample2',因为提供的Makefile
已设置为查找R, Rcpp和RInside。
- 函数调用中参数的顺序重要吗
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 变量没有改变?通过向量的函数调用
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 模板函数调用
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 析构函数调用
- 成员函数调用和C++对象模型
- 使用共享指针的函数调用,其对象应为 const
- C++:编译时检查匹配的函数调用对?
- 函数调用C++中的参数太少
- 来自 DLL 的函数调用 [表观调用的括号前面的表达式必须具有(指向-)函数类型]
- 返回指向对象的指针的函数调用是否为 prvalue?
- C++ 如何重载 [] 运算符并进行函数调用
- 代码的效率. 转到和函数调用
- 是同一作用域的函数部分中的函数调用
- 如何封装一个函数,以便它只能由同一类中的一个其他函数调用?
- 类型擦除的std::function与虚拟函数调用的开销