将 3rd 方头文件与 Rcpp 一起使用
Posted
技术标签:
【中文标题】将 3rd 方头文件与 Rcpp 一起使用【英文标题】:Using 3rd party header files with Rcpp 【发布时间】:2012-12-21 18:23:01 【问题描述】:我有一个名为 coolStuff.h
的头文件,其中包含我想在我的 cpp 源文件中使用的函数 awesomeSauce(arg1)
。
目录结构:
Rworking 目录 sourceCpp CppFile.cpp cppHeaders coolStuff.h代码:
#include <Rcpp.h>
#include <cppHeaders/coolStuff.h>
using namespace Rcpp;
// [[Rcpp::export]]
double someFunctionCpp(double someInput)
double someOutput = awesomeSauce(someInput);
return someOutput;
我得到错误:
theCppFile.cpp:2:31: error: cppHeaders/coolStuff.h: No such file or directory
我已将文件和目录移动到各处,但似乎无法正常工作。我看到到处都是使用 3rd 方标头的示例,这些示例说只需这样做:
#include <boost/array.hpp>
(来自 Hadley/devtools)
https://github.com/hadley/devtools/wiki/Rcpp
那是什么?我整个上午都在搜索,但找不到对我来说似乎很简单的事情的答案。
更新 01.11.12
好的,现在我已经弄清楚了如何在 Rstudio 中构建使用 Rcpp 的包,让我重新表述一下这个问题。我有一个独立的头文件coolStuff.h,其中包含我想在我的cpp 代码中使用的函数。
1) 我应该将coolStuff.h 放在包目录结构的什么位置,以便CppFile.cpp 可以使用它包含的功能?
2) 如何在 cpp 文件中调用coolStuff.h?再次感谢您的帮助。我从上次的谈话中学到了很多。
注意:我阅读了小插图“编写使用 Rcpp 的包”,但没有说明如何执行此操作。
答案:
好吧,让我总结一下我的问题的答案,因为它分散在这个页面上。如果我有错误的细节,请随时编辑或让我知道,我会编辑它:
因此,您找到了一个 .h
或 .cpp
文件,其中包含您要在您正在编写的与 Rcpp
一起使用的 .cpp
文件中使用的函数或其他一些代码。
让我们继续调用这个找到的代码coolStuff.h
并调用你想使用的函数awesomeSauce()
。让我们调用你正在编写的文件theCppFile.cpp
。
(在这里我应该注意,.h 文件和 .cpp 文件中的代码都是 C++ 代码,它们之间的区别在于 C++ 程序员可以让事情以正确的方式组织起来。我将讨论区别在这里,但是在 SO 上的简单搜索将引导您讨论区别。对于需要使用您找到的一些 o' 代码的 R 程序员来说,没有真正的区别。)
简而言之:您可以使用像 coolStuff.h
这样的文件,前提是它不调用其他库,方法是剪切并粘贴到 theCppFile.cpp
,或者如果您创建一个可以放置的包\src
目录中的文件与theCppFile.cpp
文件并在您正在编写的文件的顶部使用#include "coolStuff.h"
。后者更灵活,允许您在其他.cpp
文件中使用coolStuff.h
中的函数。
详情:
1) coolStuff.h
不得调用其他库。所以这意味着它不能在顶部有任何包含语句。如果是这样,我在下面详述的内容可能不起作用,并且使用调用其他库的找到的代码超出了此答案的范围。
2) 如果要编译带有sourceCpp()
的文件,则需要将coolStuff.h
剪切并粘贴到theCppFile.cpp
中。有人告诉我有例外,但sourceCpp()
旨在编译一个.cpp
文件,所以这是最好的方法。
(注意:我不保证简单的剪切和粘贴可以开箱即用。您可能需要重命名变量,或者更有可能切换正在使用的数据类型以与您在 @987654351 中使用的数据类型保持一致@. 但是到目前为止,剪切和粘贴对我来说已经很简单了,有 6 个不同的简单 .h
文件)
3) 如果您只需要在theCppFile.cpp
中使用来自coolStuff.h
的代码而不需要其他任何地方,那么您应该将其剪切并粘贴到theCppFile.cpp
中。
(我再次不保证请参阅上面关于剪切和粘贴的说明)
4) 如果您想在theCppFile.cpp
和其他.cpp
文件中使用包含在coolStuff.h
中的代码,您需要考虑构建一个包。这并不难,但可能有点棘手,因为那里有关使用 Rcpp 构建包的信息范围从您想要的任何 R 包的详尽彻底的文档(但对于新手来说,这超出了您的头顶),以及新手敏感介绍(可能会遗漏您碰巧需要的细节)。
这是我的建议:
A) 首先获取theCppFile.cpp
的版本,将coolStuff.h
中的代码剪切并粘贴到theCppFile.cpp
中,该版本与sourceCpp()
一起编译并按照您的预期工作。这不是必须的,但如果您是 Rcpp OR 包的新手,最好确保您的代码在这种简单的情况下工作,然后再转到下面更复杂的情况。
B) 现在使用 Rcpp.package.skeleton()
构建您的包或使用 RStudio 中的构建功能(强烈推荐)。您可以在hadley/devtools 或Rcpp Attributes Vignette 中找到有关使用Rcpp.package.skeleton()
的详细信息。使用 Rcpp 编写包的完整文档在 Writing a package that uses Rcpp 中,但是这个文档假设您对 C++ 的使用非常了解,并且不使用新的“属性”方式来执行 Rcpp。
如果使用 RStudio,请不要忘记“构建和重新加载”,如果您不在 RStudio 中,请不要忘记 compileAttributes()
。
C) 现在您应该在 \R 目录中看到一个名为 RcppExports.R
的文件。打开它并检查一下。在RcppExports.R
中,您应该看到\src
目录中所有.cpp 文件的R 包装函数。很甜。
D) 尝试与您在theCppFile.cpp
中编写的函数相对应的 R 函数。它有效吗?如果是这样继续。
E) 构建好包后,您可以将 coolStuff.h
移动到带有 theCppFile.cpp
的 src
文件夹中。
F) 现在您可以从theCppFile.cpp
和theCppFile.cpp
(以及您想使用coolStuff.h 代码的任何其他.cpp 文件)顶部删除剪切和粘贴代码,只需将#include "coolStuff.h"
在#include <Rcpp.h>
之后。请注意,ranker.h 周围没有括号,而是有“”。这是包含用户提供的本地文件而不是 Rcpp 或 STL 等库文件时的 C++ 约定...
G) 现在你必须重建包。在 RStudio 中,这只是 Build 菜单中的“Build & Reload”。如果你没有使用 RStudio,你应该运行 compileAttributes()
H) 现在再次尝试 R 函数,就像在步骤 D) 中所做的那样,希望它可以工作。
【问题讨论】:
#include "../cppHeaders/coolStuff.h"
没有骰子:我试过了:#include "../cppHeaders/coolStuff.h"
#include <"../cppHeaders/coolStuff.h">
#include <../cppHeaders/coolStuff.h>
仍然:: No such file or directory
如果你的结构和描述的一样,它应该可以工作。名称或包含路径中是否有拼写错误?您的文件系统是否区分大小写并且您弄错了大小写?
在一个包中,您需要提供一个 Makevars 来将目录添加到编译中,例如PKG_CPPFLAGS += -I../inst/include/
很好的答案 - 感谢您提供详细信息。在相关的文章中,我发现 Hadley 的 reference 在编写包以在细节和可读性之间取得良好的平衡。特别是,它以非常友好的方式讨论了在包中使用 Rcpp。
【参考方案1】:
问题在于sourceCpp
被明确设计为仅构建一个独立的源文件。如果您希望 sourceCpp
具有依赖关系,那么它们需要是:
在系统中包含目录(即/usr/local/lib
或/usr/lib
);或
在您在Rcpp::depends
属性中列出的 R 包中
正如 Dirk 所说,如果您想要构建多个源文件,那么您应该考虑使用 R 包而不是 sourceCpp
。
请注意,如果您正在处理一个包并对包的 src 目录中的文件执行 sourceCpp,它将构建它就好像它在包中(即您可以包含来自src 目录或 inst/include 目录)。
【讨论】:
好的,我开始了。我如何查看 .h 文件并确定它是否是 Dirk 在下面提到的简单情况,或者它是否需要构建它自己的源文件,因此我不能简单地 #include 它?鉴于那里有大量“准备就绪”的 C++ 文件,似乎有人遇到一个做他们需要的事情然后想要使用该文件的文件是很常见的。我知道我的词汇不太对,但我希望你仍然能听懂我在说什么。我是一个长期的 R 人,试图将我非常慢的包转换为 Rcpp。 当然,在花了一整天试图弄清楚为什么我的文件路径错误之后,我本可以编写我自己发现的函数。但希望这将在未来帮助我并帮助其他人。 我能够使用头文件,只需将其所有内容直接粘贴到我的 cpp 文件中即可。然后 sourceCpp 顺利地编译了它。这似乎是一种非常“非模块化”的编程方式,因为我可能希望在其他 cpp 文件中使用相同的功能。这将不必要地在整个地方创建重复的代码。另外,我想在我编写的其他 cpp 文件中使用我自己的一些 cpp 代码。这将使更改内容非常耗时。 真的没有办法像我们在运行时在 R 中使用 source() 那样在编译时从其他文件中提取 cpp 代码吗?无论如何,如果您尝试 sourceCpp 包含#inclide "filename.h"
的文件时产生的错误不是那么具有误导性,那就太好了。当错误提示 theCppFile.cpp:2:31: error: filename.h: No such file or directory
时,我假设的最后一件事是编译器(或者它是 sourceCpp() 包装器)不喜欢这样一个事实,即恰好不在 @ 中的文件的 #include 987654329@ 或 /usr/lib
.
基本理念是,在 R 中有一个用于构建多个源文件的系统,这些源文件相互依赖(和其他库),这就是包系统。 sourceCpp 背后的想法不是为包创建一个替代的并行系统,而是提供一种无需构建包即可编写单个函数或源文件的简单方法。我知道您正在寻找一个中间立场,也许这是正确的做法,但我们希望谨慎地采取这些步骤。【参考方案2】:
我可以通过在调用 sourceCpp 之前设置两个环境变量来链接任何库(在这种情况下为 MPFR):
Sys.setenv("PKG_CXXFLAGS"="-I/usr/include")
Sys.setenv("PKG_LIBS"="-L/usr/lib/x86_64-linux-gnu/ -lm -lmpc -lgmp -lmpfr")
第一个变量包含库头的路径。第二个包括库二进制文件的路径及其文件名。在这种情况下,还需要其他依赖库。有关更多详细信息,请查看 g++ 编译和链接 flags。这些信息通常可以使用 pkg-config 获得:
pkg-config --cflags --libs mylib
为了更好地理解,我建议使用带有详细输出的 sourceCpp 来打印 g++ 编译和链接命令:
sourceCpp("mysource.cpp", verbose=TRUE, rebuild=TRUE)
【讨论】:
这个答案是最普遍的。感谢您提供详细信息!【参考方案3】:在调用 sourceCpp 之前,我能够在 R 中使用以下全局命令链接一个 boost 库
Sys.setenv("PKG_CXXFLAGS"="-I \path-to-boost\")
基本上反映了这篇文章,但使用了不同的编译器选项:http://gallery.rcpp.org/articles/first-steps-with-C++11/
【讨论】:
不,你没有链接,你包括头文件,这些头文件碰巧可以在没有链接的情况下使用。【参考方案4】:几件事:
您主题中的“第三方头库”没有意义。
第三方标头可以通过模板代码工作,其中标头就是您所需要的,即只有一个包含步骤,编译器会解决问题。
一旦您需要库和目标代码的实际链接,您可能无法使用强大而有用的sourceCpp
,除非您通过插件(或环境变量)为其提供元信息。
那么在这种情况下,写一个包。
简单的事情就是使用 Rcpp 和新属性,或者旧的内联和 cxxfunction
。更复杂的使用 --- 而外部库是更复杂,您需要查阅文档。为此,我们在 Rcpp 中添加了几个小插曲。
【讨论】:
我修正了标题。我想我属于情况(2)而不是情况(3),但我不确定。它只是一个包含 3 个函数的 .h 文件。一个人怎么知道他们是否可以#include 它然后去,或者他们是否必须构建一个包?这是在某处记录的吗?我正在构建一个包,但是我现在只是对所有内容进行原型设计,并且我不想每次调试一个小的 cpp 文件时都必须重新构建我的包。【参考方案5】:尖括号用于系统包含,例如标准库。
对于您自己项目的本地文件,请使用引号:“”。
另外,如果您将头文件放在不同的目录中,则头文件路径应指定为包含它的源文件的本地路径。
因此,对于您的示例,这应该可行:
#include "../cppHeaders/coolStuff.h"
您可以配置搜索路径,这样就可以在不这样做的情况下找到文件,但通常只值得为您想要包含在多个项目中的内容这样做,否则会期望有人“安装”。
【讨论】:
感谢您对 和 "" 的解释,但这仍然不起作用。我什至将coolStuff 的副本与CppFile.cpp 和Rworking 目录中的副本放在同一个文件夹中。然后我尝试了:#include "coolStuff.h"
仍然得到错误。【参考方案6】:
我们可以通过在.R/Makevars
文件的PKG_CXXFLAGS
变量中写入路径来添加它,如下所示。以下是在macOS中添加随Anaconda一起安装的xtensor
的头文件的示例。
⋊> ~ cat ~/.R/Makevars
CC=/usr/local/bin/gcc-7
CXX=/usr/local/bin/g++-7
CPLUS_INCLUDE_PATH=/opt/local/include:$CPLUS_INCLUDE_PATH
PKG_CXXFLAGS=-I/Users/kuroyanagi/.pyenv/versions/miniconda3-4.3.30/include
LD_LIBRARY_PATH=/opt/local/lib:$LD_LIBRARY_PATH
CXXFLAGS= -g0 -O3 -Wall
MAKE=make -j4
【讨论】:
【参考方案7】:这在 Windows 中对我有用:
Sys.setenv("PKG_CXXFLAGS"='-I"C:/boost/boost_1_66_0"')
编辑:实际上,如果您使用 Boost Headers,则不需要这个(感谢 Ralf Stubner):
// [[Rcpp::depends(BH)]]
【讨论】:
以上是关于将 3rd 方头文件与 Rcpp 一起使用的主要内容,如果未能解决你的问题,请参考以下文章