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/Makevarsspecifies how you build source package and standalone C++ filesrc/Makevarsspecifies 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.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, sinceLinkingTois 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 {}; Rcppzhuoer::foo<void> foo1 {}; // comment me in release };想用新标准,还是 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)]]的源代码.plugins[["cpp17"]] <- function() { 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 setincludesparameter, still fail.Thanks for
cacheDir = 'tests/testthat', I can get the temp.cppfile, 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)↩︎