7 Rcpp
7.1 core idea
only use Rcpp as a tool to pass object:
- write your code logical in C++ way, use STL as much as possible
- only use Rcpp function for very simple operation, like
.size()
,[i]
7.2 complier options
There are two files:
~/.R/Makevars
specifies how you build source package and standalone C++ filesrc/Makevars
specifies how your package is built by others
Typically, you can use ~/.R/Makevars
to switch which complier to use 14:
CC=clang
CXX17 = clang++
while C++ version (like C++17) should be specified per .cpp
file using // [[Rcpp::plugins(cpp17)]]
, since you can’t determine which version others’ code conform to.
7.3 internals
IntegerVector
等都可以看作是 shadow copy,记住不能 move
7.4 current limit
Rcpp amis to bring high-performance computering into R, however, I desire many C++ native feature thus 碰了很多次壁。
pure C++ function can’t be exported. Since you can only export R version or R & C++ version, but functions like
void foo(std::list<int> bar)
can’t convert to R version.exporting a header file which depends on a header file from another package is impossible ^[for paristools, the final solution is to give up exporting anything in header file. Since we find we needn’t export functions like
as_locs()
at all, we needn’t export the defination of custom struct.in a standalone file
// [[Rcpp::depends(paristools)]] #include <paristools.h>
would cause
In file included from /home/zhuoer/.local/lib/R/paristools/include/paristools.h:7: /home/zhuoer/.local/lib/R/paristools/include/paristools.hpp:4:10: fatal error: 'Rcppzhuoer.h' file not found #include <Rcppzhuoer.h> ^~~~~~~~~~~~~~
That’s because although paristools depends on Rcppzhuoer,
Rcpp::depends()
only adds the former to include path.The situation is better if you use
<paristools.h>
in a package, sinceLinkingTo
is recursivetestthat can’t test C++ export
Rcpp use
find.package()
to determine package path, when you use testthatfind.package('pkg')
return the project dir (in normal session, it return package in the library,.local/lib/R/pkg
). The reason is thatfind.package()
looks for loaded namesace first, and testthat load pkg namespace from the the project dir. [^debug-testthat-cpp-export]A work-around is to use
callr::r()
.
7.4.1 坑边杂谈
以下为作死记录,可能会移到 blog
标准库和编译器对右值的优化根本轮不到我来操心
刚开始写 Rcpp 时,我很在意效率,甚至还返回右值引用等,结果还招致了
complier warning
,查了半天资料还去不掉。最后我放弃了,改为写了一个struct
,Rcppzhuoer::foo
, 其移动构造、复制都会打印一条消息,用来检测有没有浪费。比如我要用到如下的自定义类型,就把Rcppzhuoer::foo
作为一个成员。结果根本没有一点浪费,只需要在必要的时候加上std::move()
就好了struct bar { std::string messsage {}; int count {}; void> foo1 {}; // comment me in release Rcppzhuoer::foo< };
想用新标准,还是 std 大法好
之前启用 C++17 时,我曾用
grep "std=" /usr/lib/R/etc/Makeconf
来搜索 R 定义的 alias,然后把PKG_CXXFLAGS = $(CXX17STD)
添加到src/Makevars
文件。但是 R 手册提到It should be written if at all possible in a portable style, in particular (except for
Makevars.win
) without the use of GNU extensions.而
CXX17STD = -std=gnu++17
,所以我就改为直接用PKG_CXXFLAGS = -std=c++17
了Rcpp::plugins()
没毛病之前用不了 clang++,我翻出了
// [[Rcpp::plugins(cpp17)]]
的源代码"cpp17"]] <- function() { .plugins[[if (getRversion() >= "3.4") # with recent R versions, R can decide list(env = list(USE_CXX17 = "yes")) else list(env = list(PKG_CXXFLAGS ="-std=c++17")) }
当时我觉得是
USE_CXX17 = "yes"
导致 Rcpp 只能使用 g++,还修改了 Rcpp 的源代码。后来看来,应该是我弄错了。这里把我翻到的代码贴出来,以便以后查阅。debug why testthat can’t test C++ export
Thanks for
verbose = T
, I find the code doesn’t include#include <paristools.h>
(later I find Rcpp can’t find the file, so it doesn’t include). After setincludes
parameter, still fail.Thanks for
cacheDir = 'tests/testthat'
, I can get the temp.cpp
file, and directly runclang++
command. Finally, I found the cause.
Formerly I use
CC=clang CXX = clang++ CXX98 = clang++ CXX11 = clang++ CXX14 = clang++ CXX17 = clang++ CXX1X = clang++
But later I found some packages is only compatible with
g++
(such as DropletUtils 1.2.2), so I choose to only useclang++
for C++17, assuming that those bad packages won’t use latest standard (while myself always do that)↩︎