Want to share your content on R-bloggers? click here if you have a blog, or here if you don’t.
About
From cpp11 description: “Provides a header only, C++11 interface to R’s C interface. Compared to other approaches ‘cpp11’ strives to be safe against long jumps from the C API as well as C++ exceptions, conform to normal R function semantics and supports interaction with ‘ALTREP’ vectors.”
I have used cpp11 for two years right after I started learning C++ with no previous C/C++ knowledge. Now I have suggested the following changes to the codebase to improve the user experience and reduce the number of lines of code needed to perform common tasks. I excluded describing PRs that only relate to technical aspects or tests.
PRs
Convert logicals to integers and doubles
as_integers()
and as_doubles()
now understand logical inputs (#426).
Here is an example of something that returned an error before:
test_that("as_doubles(logicals)") { cpp11::writable::logicals y; for (int i = 0; i < 4; i++) { y.push_back(i % 2 == 0); } cpp11::doubles i(cpp11::as_doubles(y)); expect_true(i[0] == 1.0); expect_true(i[1] == 0.0); expect_true(i[2] == 1.0); expect_true(i[3] == 0.0); expect_true(cpp11::detail::r_typeof(i) == REALSXP); }
Improving string vector performance for push_back and subscript assignment
I added refactors and test that translate into a push_back
that is closer to a 1:1 speed ratio than 1:4 compared to direct assignment (#430).
Previously, the push_back
was 4 times slower than direct assignment because of protections applied in cases when there is immediate assignment with no translation.
# A tibble: 14 × 6 expression len min mem_alloc n_itr n_gc <bch:expr> <int> <bch:tm> <bch:byt> <int> <dbl> 1 assign_cpp11_(n = len, 123L) 1000000 590.79ms 21.63MB 12 8 2 assign_rcpp_(n = len, 123L) 1000000 441.09ms 7.63MB 15 5 # A tibble: 3 × 6 expression len min mem_alloc n_itr n_gc <bch:expr> <int> <bch:tm> <bch:byt> <int> <dbl> 1 grow_strings_cpp11_(len, 123L) 1000000 462ms 23.63MB 7 13 2 grow_strings_rcpp_(len, 123L) 1000000 453ms 7.63MB 16 4 3 grow_strings_manual_(len, 123L) 1000000 438ms 23.63MB 8 12
Convert ordered and unordered C++ maps to R lists
Ordered and unordered C++ maps are converted to R lists now (#437).
Here is an example of something that was not possible before:
[[cpp11::register]] SEXP ordered_map_to_list_(cpp11::doubles x) { std::map<double, int> counts; int n = x.size(); for (int i = 0; i < n; i++) { counts[x[i]]++; } return cpp11::as_sexp(counts); }
Correctly set names for matrices
Previously, cpp11 ignored the column or row names nor allowed to define those from C++ side for a doubles_matrix
or integers_matrix
, except if it was converted to a SEXP
(#428).
Here is an example of the correction:
[[cpp11::register]] cpp11::doubles_matrix<> mat_mat_create_dimnames() { cpp11::writable::doubles_matrix<> out(2, 2); out(0, 0) = 1; out(0, 1) = 2; out(1, 0) = 3; out(1, 1) = 4; cpp11::writable::list dimnames(2); dimnames[0] = cpp11::strings({"a", "b"}); dimnames[1] = cpp11::strings({"c", "d"}); out.attr("dimnames") = dimnames; return out; }
Copy complex numbers, vectors or matrices from R to C++ and viceversa
Previously, I was passing complex numbers from R to C++ and viceversa by converting them to a list with the real part and the imaginary part expressed as the first and second vectors of the list. Now it is possible to pass them directly (#427).
Here is an example of something that was not possible before:
test_that("vector objects can be created, filled, and copied") { cpp11::writable::complexes v(2); v[0] = std::complex<double>(1, 2); v[1] = std::complex<double>(3, 4); cpp11::complexes vc = v; expect_true(v.size() == vc.size()); for (int i = 0; i < 2; ++i) { expect_true(v[i] == vc[i]); } }
Document functions with Roxygen directly in C++ scripts
In order to reduce clutter in my workflow, I added some code to be able to roxygenise directly in the cpp files rather than document the functions by separate (#440).
Here is an example of something that was not possible before:
#include "cpp11/doubles.hpp" using namespace cpp11; /* roxygen start @title Roxygenised x plus 1 @param x numeric value @description Dummy function to test roxygen2. It adds 1.0 to a double. @export @examples roxcpp_(1.0) roxygen end */ [[cpp11::register]] double roxcpp_(double x) { double y = x + 1.0; return y; }
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you’re looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don’t.
Continue reading: Cpp11 pull requests to improve the integration of R and C++
Key Points from the Cpp11 Pull Requests and The Future of Integration R and C++
The author of this article shares insightful improvements and additions in the cpp11 library over the past couple of years, which aid the integration of R and C++. These changes were aimed to simplify common tasks and augment the user experience.
Converting Logicals to Integers and Doubles
First of all, the adjustments made in the cpp11 library now allow for the conversion of logical inputs into integers and doubles with the help of as_integers() and as_doubles() functions (#426). This feature helps avoid occurrence of an error that was experienced previously during such conversions.
Improving String Vector Performance
Secondly, the performance of string vector has been substantially enhanced when it comes to push_back and subscript assignment. The author talks about how the restructuring and tests lead to a push_back that has a speed ratio relatively comparable to that of direct assignment (#430). Previously, it was four times slower due to protections applied such that issues with direct assignments can be prevented without the need for translations.
Conversion of C++ Maps to R Lists
Also, the cpp11 library now functions to convert ordered and unordered C++ maps into R lists (#437). This development presents a significant boost in easing operations that involve working with both these data types.
Proper Naming Set for Matrices
Furthermore, the facility to set the correct names, especially for columns and rows of the matrices while using the cpp11 library, has been introduced (#428). The library did not support defining these directly from the C++ side earlier. This is particularly useful when dealing with doubles_matrix or integers_matrix.
Enhancing Manipulation of Complex Numbers, Vectors, and Matrices
The cpp11 library has also improved the manipulation of complex numbers, vectors, and matrices when interfacing between R and C++ (#427). The updates allow for a more efficient transition of these complex data types between the two languages.
Introducing Roxygen Function Documentation
Lastly, the library now allows users to document functions with Roxygen directly within C++ scripts (#440). This eliminates the need to document functions separately, making the workflow more efficient.
Implications and Future Developments
The above changes in the cpp11 library betoken an important shift towards a more efficient and smooth interaction between R and C++. This could lead to more widespread use of R with C++ to take advantage of the speed and efficiency of C++, while enjoying the flexibility and simplicity of R language. Future advancements could involve further improving the interface between these two languages. Wholly performance-based improvements such as string vector enhancements are bound to be a primary focus.
Actionable Advice
In order to maximize the benefits of these improvements, users should consider:
- Familiarizing themselves with the latest changes made in the cpp11 library.
- Practice implementing these new additions in their current projects to understand their functionality better.
- Staying updated with all future enhancements and updates.
Additionally, developers should contemplate focusing on refining the library further by regularly testing it extensively to ensure it works efficiently and optimally. Also, they should contemplate the needs of the users and continue making improvements to enhance the user experience.