为了说明,让我们考虑一个具体的例子,任何人都应该能够运行…
下面的代码是一个非常简单的cxxfunction,只需将输入向量加倍.但是请注意,还有一个额外的变量myvar可以更改值几次,但不会影响输出 – 这已经被添加,以便我们可以看到调试进程何时正确运行.
library(inline) library(Rcpp) f0 <- cxxfunction(signature(a="numeric"),plugin="Rcpp",body=' Rcpp::NumericVector xa(a); int myvar = 19; int na = xa.size(); myvar = 27; Rcpp::NumericVector out1(na); for(int i=0; i < na; i++) { out1[i] = 2*xa[i]; myvar++; } myvar = 101; return(Rcpp::List::create( _["out1"] = out1)); ')
在我们运行上面之后,键入命令
getLoadedDLLs()
在R会话中列出DLL列表.列出的最后一个应该是由上述过程创建的DLL – 它有一个随机的临时名称,在我的例子中
file7e61645c
“文件名”列显示cxxfunction已将此DLL放在位置tempdir()中,这对我来说是当前的
C:/Users/TimP/AppData/Local/Temp/RtmpXuxtpa/file7e61645c.dll
> f0(c(-7,0.7,77)) $out1 [1] -14.0 1.4 154.0
但是我们当然也可以使用.Call命令直接通过名称来调用DLL:
> .Call("file7e61645c",c(-7,77)) $out1 [1] -14.0 1.4 154.0
所以我已经达到了直接用R输入(这里是向量c(-7,77))直接调用独立DLL,并将其正确地返回给R的那一点.
我真正需要的是一个逐行调试(使用gdb,我推测)的设施,这将允许我观察myvar的值设置为19,27,28,29,30,最后101随着代码的进行.上面的例子是故意设置的,所以调用DLL告诉我们没有关于myvar.
为了澄清,这里的“胜利条件”是能够观察myvar的变化(看到值myvar = 19将是第一步!),而不添加任何其他代码的身体.这显然可能需要更改编译代码的方式(调试模式设置是否打开?)或者R调用方式 – 但是我不知道从哪里开始.如上所述,所有这些都是基于Windows的.
最后注意事项:在我的实验中,我实际上对cxxfunction的一个副本进行了一些小的修改,以便输出DLL及其中的代码接收用户定义的名称,并且位于用户定义的目录中,而不是临时名称和位置.但这并不影响问题的本质.我提到这只是为了强调,如果有人给我一个微调,它应该是相当容易的改变编译设置:)
为了完整起见,在上面的原始cxxfunction调用中设置verbose = TRUE,将显示以下形式的编译参数:
C:/R/R-2.13.2/bin/i386/R CMD SHLIB file7e61645c.cpp 2> file7e61645c.cpp.err.txt g++ -I"C:/R/R-213~1.2/include" -I"C:/R/R-2.13.2/library/Rcpp/include" -O2 -Wall -c file7e61645c.cpp -o file7e61645c.o g++ -shared -s -static-libgcc -o file7e61645c.dll tmp.def file7e61645c.o C:/R/R-2.13.2/library/Rcpp/lib/i386/libRcpp.a -LC:/R/R-213~1.2/bin/i386 -lR
我的修改版本具有与上述相同的编译参数,除了字符串“file7e61645c”被用户选择的名称(例如“testdll”)替换,并将相关文件复制到更永久的位置.
提前感谢你的帮助:)
但这是否意味着我们应该使用它来进行每一项任务?这是否意味着它没有“成本”,如临时目录等中的随机文件名?罗曼和我在我们的文件中另有争议.
最后,动态加载的R模块的调试比较困难.在(强制性)Writing R Extensions中有一个关于它的整个部分,Doug Bates一两次发布了关于如何通过ESS和Emacs(尽管我总是忘记他发布的地方)的教程;曾经是07年3月的IIRC.
编辑2012年7月 – 7月:
这是你的一步一步:
(序言:我已经使用gcc和g了很多年了,甚至当我添加-g时,我并不总是把-O2变成-O0,我真的不确定你需要这个,但是你要的是. ..)
>将您的环境变量CXXFLAGS设置为“-g -O0 -Wall”.有许多方法可以做到这一点,一些是平台依赖的(例如Windows控制面板),因此不那么普遍和有趣.我在Windows和Unix上使用〜/ .R / Makevars.您可以使用它,或者您可以覆盖R的系统级$RHOME / etc / Makeconf,或者您可以使用Makeconf.site或…查看完整的文档—但正如我所说,〜/ .R / MakeVars是我的优先的方式,因为它不干扰在R外的编译.
>现在每个编译R通过R CMD SHLIB,R CMD COMPILE,R CMD INSTALL,…将使用.因此,您不再需要使用内联或本地软件包.继续内联…
>对于其余的,我们主要遵循“编写R扩展”中的第4.4.1节“动态加载代码中的入口点”:
>使用R -d gdb启动另一个R会话.
>编译你的代码对于
06000
我得到
[...] Compilation argument: /usr/lib/R/bin/R CMD SHLIB file11673f928501.cpp 2> file11673f928501.cpp.err.txt ccache g++-4.6 -I/usr/share/R/include -DNDEBUG -I"/usr/local/lib/R/site- library/Rcpp/include" -fpic -g -O0 -Wall -c file11673f928501.cpp -o file11673f928501.o g++-4.6 -shared -o file11673f928501.so file11673f928501.o -L/usr/local/lib/R/site-library/Rcpp/lib -lRcpp -Wl,-rpath,/usr/local/lib/R/site-library/Rcpp/lib -L/usr/lib/R/lib -lR
>调用例如tempdir()来查看临时目录,cd到上面使用的这个临时目录和dyn.load()上面构建的文件:
06002
>现在通过发送中断信号暂停R(在Emacs中,从下拉菜单中选择一个简单的选项).
>在gdb中,设置一个断点.上面的单个作业成为我的第32行,所以
06003
fun()
> Presto,在我们想要的断点处的调试器中:
06004
>现在,“只是”你的工作gdb的魔法
现在,正如我在第一次尝试中所说的那样,通过Rcpp.package.skeleton()可以为您编写的一个简单的包,所有这一切将更容易(在我看来),因为您不必处理随机的目录和文件名.但每一个到自己的…