In many cases there is no need to convert a package from Rcpp. If the code is already written and you don’t have a very compelling need to use cpp11 I would recommend you continue to use Rcpp. However if you do feel like your project will benefit from using cpp11 this vignette will provide some guidance and doing the conversion.
Add cpp11 by calling
Start converting function by function.
Converting the code a bit at a time (and regularly running your tests) is the best way to do the conversion correctly and make progress. Doing a separate commit after converting each file (or possibly each function) can make finding any regressions with git bisect much easier in the future.
Rcpp::and replace with the equivalent cpp11 function using the cheatsheets below.
@importFrom Rcpp sourceCpp.
src/Makevarsif it only contains
|Rcpp||cpp11 (read-only)||cpp11 (writable)|
Note that each cpp11 vector class has a read-only and writeable
version. The default classes, e.g.
read-only classes that do not permit modification. If you want
to modify the data you or create a new vector, use the writeable
Another major difference in Rcpp and cpp11 is how vectors are grown.
Rcpp vectors have a
push_back() method, but unlike
std::vector() no additional space is reserved when pushing.
This makes calling
push_back() repeatably very expensive,
as the entire vector has to be copied each call. In contrast
cpp11 vectors grow efficiently, reserving extra space. See
for more details.
Rcpp also allows very flexible implicit conversions, e.g. if you pass
REALSXP to a function that takes a
Rcpp::IntegerVector() it is implicitly converted to a
INTSXP. These conversions are nice for usability, but
require (implicit) duplication of the data, with the associated runtime
costs. cpp11 throws an error in these cases. If you want the implicit
coercions you can add a call to
as.double() as appropriate from R when you call the
cpp11::warning() are thin wrappers around
Rf_warning(). These are simple C
functions with a
printf() API, so they do not understand
C++ objects like
std::string. Therefore you need to call
obj.c_str() when passing string data to them.
Calling R functions from C++ is similar to using Rcpp.
// Rcpp ----------------------------------------------- Rcpp::Function as_tibble("as_tibble", Rcpp::Environment::namespace_env("tibble")); as_tibble(x, Rcpp::Named(".rows", num_rows), Rcpp::Named(".name_repair", name_repair)); // cpp11 ----------------------------------------------- using namespace cpp11::literals; // so we can use ""_nm syntax auto as_tibble = cpp11::package("tibble")["as_tibble"]; as_tibble(x, ".rows"_nm = num_rows, ".name_repair"_nm = name_repair);
Rcpp includes calls to
PutRNGstate() around the wrapped function. This ensures
that if any C++ code calls the R API functions
R_unif_index() the random seed state is set accordingly.
cpp11 does not do this, so you must include the calls to
yourself if you use any of those functions in your C++ code.
6.3 - Random number generation for details on these functions.
One convenient way to do safely is to use a simple class:
Rcpp.h includes a number of STL headers automatically, notably
<vector>, however the
cpp11 headers generally do not. If you have errors like
error: no type named 'string' in namespace 'std'
You will need to include the appropriate STL header, in this case
If you see something like this:
In file included from file.cpp:1: In file included from path/cpp11/include/cpp11.hpp:3: path/cpp11/include/cpp11/R.hpp:12:9: warning: 'STRICT_R_HEADERS' macro redefined [-Wmacro-redefined] #define STRICT_R_HEADERS
Make sure to remove
cpp11 conflicts with macros declared by some R headers unless the
defined. If you include
cpp11.hpp (or, at a minimum,
cpp11/R.hpp) before any R headers these macros will be
defined appropriately, otherwise you may see errors like
R headers were included before cpp11 headers and at least one of R_NO_REMAP or STRICT_R_HEADERS was not defined.
Which indicate that you must either change your include order or add
preprocessor definitions for
STRICT_R_HEADERS. Note that transitive includes of R
headers (for example, those included by
Rcpp.h) can also
introduce the conflicting macros.
If you use typedefs for cpp11 types or define custom types you will
need to define them in a
pkgname_types.hpp file so that
cpp_register() can include it in the generated code.
If you are constructing a length 1 logical vector you may need to
explicitly use a
r_bool() object in the initializer list
NA_INTEGER. This issue only occurs with the clang compiler,
not gcc. When constructing vectors with more than one element this is
not an issue