├── .Rbuildignore ├── .gitignore ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── RcppExports.R └── hello.R ├── README.Rmd ├── README.md ├── RcppStdVector.Rproj ├── inst └── include │ └── RcppStdVector.h ├── man ├── figures │ └── README-pressure-1.png ├── hello.Rd └── rcpp_hello.Rd └── src ├── Makevars ├── RcppExports.cpp └── RcppStdVector.cpp /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | src/*.o 6 | src/*.so 7 | src/*.dll 8 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RcppStdVector 2 | Type: Package 3 | Title: Efficient Standard Vector With Rcpp 4 | Version: 0.1.0 5 | Author: Tim Keitt 6 | Maintainer: Tim Keitt 7 | Description: Passing std::vector in Rcpp entails a full copy of its contents. This package provides a custom allocator that when used with std::vector allows zero-copy on return. 8 | License: MIT + file LICENSE 9 | Encoding: UTF-8 10 | LazyData: true 11 | Imports: Rcpp (>= 0.12.9) 12 | LinkingTo: Rcpp 13 | RoxygenNote: 6.1.1 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2019 2 | COPYRIGHT HOLDER: Tim Keitt 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 Tim Keitt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | exportPattern("^[[:alpha:]]+") 2 | importFrom(Rcpp, evalCpp) 3 | useDynLib(RcppStdVector) 4 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # RcppStdVector 0.1.0 2 | 3 | * Added a `NEWS.md` file to track changes to the package. 4 | -------------------------------------------------------------------------------- /R/RcppExports.R: -------------------------------------------------------------------------------- 1 | # Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | test_alloc <- function() { 5 | .Call('_RcppStdVector_test_alloc', PACKAGE = 'RcppStdVector') 6 | } 7 | 8 | test_modify <- function(x) { 9 | .Call('_RcppStdVector_test_modify', PACKAGE = 'RcppStdVector', x) 10 | } 11 | 12 | test_list <- function(x) { 13 | .Call('_RcppStdVector_test_list', PACKAGE = 'RcppStdVector', x) 14 | } 15 | 16 | test_char <- function() { 17 | .Call('_RcppStdVector_test_char', PACKAGE = 'RcppStdVector') 18 | } 19 | 20 | test_copy_chr <- function(x) { 21 | .Call('_RcppStdVector_test_copy_chr', PACKAGE = 'RcppStdVector', x) 22 | } 23 | 24 | test_copy_rcpp_chr <- function(x) { 25 | .Call('_RcppStdVector_test_copy_rcpp_chr', PACKAGE = 'RcppStdVector', x) 26 | } 27 | 28 | test_native <- function(x) { 29 | .Call('_RcppStdVector_test_native', PACKAGE = 'RcppStdVector', x) 30 | } 31 | 32 | test_double_it <- function(x) { 33 | .Call('_RcppStdVector_test_double_it', PACKAGE = 'RcppStdVector', x) 34 | } 35 | 36 | test_inplace <- function(x) { 37 | invisible(.Call('_RcppStdVector_test_inplace', PACKAGE = 'RcppStdVector', x)) 38 | } 39 | 40 | test_copy_ivec <- function(x, y) { 41 | invisible(.Call('_RcppStdVector_test_copy_ivec', PACKAGE = 'RcppStdVector', x, y)) 42 | } 43 | 44 | test_copy_const_ivec <- function(x) { 45 | .Call('_RcppStdVector_test_copy_const_ivec', PACKAGE = 'RcppStdVector', x) 46 | } 47 | 48 | test_no_copy_construct_ivec <- function(x) { 49 | invisible(.Call('_RcppStdVector_test_no_copy_construct_ivec', PACKAGE = 'RcppStdVector', x)) 50 | } 51 | 52 | -------------------------------------------------------------------------------- /R/hello.R: -------------------------------------------------------------------------------- 1 | # Hello, world! 2 | # 3 | # This is an example function named 'hello' 4 | # which prints 'Hello, world!'. 5 | # 6 | # You can learn more about package authoring with RStudio at: 7 | # 8 | # http://r-pkgs.had.co.nz/ 9 | # 10 | # Some useful keyboard shortcuts for package authoring: 11 | # 12 | # Build and Reload Package: 'Cmd + Shift + B' 13 | # Check Package: 'Cmd + Shift + E' 14 | # Test Package: 'Cmd + Shift + T' 15 | 16 | hello <- function() { 17 | print("Hello, world!") 18 | } 19 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | output: github_document 3 | --- 4 | 5 | 6 | 7 | ```{r, include = FALSE} 8 | knitr::opts_chunk$set( 9 | collapse = TRUE, 10 | comment = "#>", 11 | fig.path = "man/figures/README-", 12 | out.width = "100%" 13 | ) 14 | ``` 15 | # RcppStdVector 16 | 17 | 18 | [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) 19 | 20 | 21 | The ```std::vector``` container is the cornerstone of the C++ Standard Library. When used with Rcpp however, there is an inherent cost in using ```std::vector``` as memory must be copied both from and to R internal data structures. The RcppStdVector package allows users to write Rcpp code using the Standard Library ```std::vector``` container without incurring an extra container copy operation. Consider the following: 22 | ``` 23 | std::vector 24 | my_func(std::vector& x) { // copy from REALSXP to std::vector here 25 | ... 26 | return x; // additional copy from std::vector to REALSXP here 27 | } 28 | ``` 29 | For large vectors, this entails a large overhead. Rcpp native vectors avoid these copies by holding an ```SEXP```, which will be only copied on demand. Recall however that unless an Rcpp vector is duplicated, functions can modify the vector in-place, which violates the no-side-effects rule. This can be useful or problematic in cases where the ```SEXP``` is aliased to several R variables. Most modifying Rcpp functions will therefore usually implement cloning the input vector. Similarly, using RcppStdVector, we can write: 30 | ``` 31 | RcppStdVector::std_vec_real 32 | my_func(RcppStdVector::std_vec_real& x) { // copy here from REALSXP to std::vector 33 | ... 34 | return x; // zero-overhead, no-copy return of REALSXP 35 | } 36 | ``` 37 | The zero-overhead return is achieved by using a custom allocator that allocates an R vector instead of general heap memory. This R vector is simply retrieved from the allocator at the end. Special consideration is taken when the capacity of the ```std::vector``` is greater than its size so that there are no dangling elements. It is furthermore possible to construct a standard vector in-place over an R vector via placement new. 38 | ``` 39 | void my_func(RcppStandardVector::std_ivec_int& x) { // no copy 40 | // modify elements directly 41 | } 42 | ``` 43 | The caveat to this is that any operation that causes allocation could lead to undefined behavior as the in-place allocator does not actually allocate and delete. Operations that grow the vector will throw an error. Provided overloads for assignment and copy construction will also throw. 44 | 45 | There are several reasons one might wish to use ```std::vector``` over the native Rcpp vector types. First, as part of the Standard Library, ```std::vector``` is simple and well-documented. Compare [this](https://en.cppreference.com/w/cpp/container/vector) to [this](http://dirk.eddelbuettel.com/code/rcpp/html/classRcpp_1_1Vector.html). Second, using ```std::vector``` everywhere minimizes code rewrites when moving between other C++ codebases and Rcpp. Third, ```std::vector``` supports amortized-constant appending elements. This is a very common idiom in C++ coding and its absense in Rcpp is a source of frustration. Consider the following functions: 46 | ``` 47 | SEXP test_copy_chr(Rcpp::CharacterVector& x) { // no copy 48 | RcppStdVector::std_vec_chr y; // this IS a std::vector 49 | auto oi = std::back_inserter(y); // amortized-constant insertion 50 | std::copy(x.begin(), x.end(), oi); 51 | return Rcpp::wrap(y); // no copy 52 | } 53 | 54 | SEXP test_copy_rcpp_chr(Rcpp::CharacterVector& x) { // no copy 55 | Rcpp::CharacterVector y; // Rcpp type 56 | auto oi = std::back_inserter(y); // copies entire vector on each insertion 57 | std::copy(x.begin(), x.end(), oi); 58 | return Rcpp::wrap(y); // no copy 59 | } 60 | ``` 61 | On my system, I get: 62 | ``` 63 | > microbenchmark(r1 <- test_copy_chr(x), r2 <- test_copy_rcpp_chr(x)) 64 | Unit: microseconds 65 | expr min lq mean median uq max 66 | r1 <- test_copy_chr(x) 94.811 102.117 124.4515 125.6335 129.7775 218.154 67 | r2 <- test_copy_rcpp_chr(x) 935634.546 975367.489 1012093.6482 996092.4005 1023481.6770 1228233.548 68 | ``` 69 | where ```x``` contains 1e4 character strings. The sole drawback of this idiom is that up to 50% of the memory allocated to the ```std::vector``` may be unused. 70 | 71 | RcppStdVector supports list and string-list types. For example the following copies a list of elements and duplicates each. 72 | ``` 73 | template 74 | void dup_vec(SEXP& s) { 75 | auto y = RcppStdVector::from_sexp(s); 76 | auto oi = std::back_inserter(y); 77 | y.reserve(2 * y.size()); 78 | std::copy(RcppStdVector::begin(s), 79 | RcppStdVector::end(s), oi); 80 | s = PROTECT(Rcpp::wrap(y)); 81 | } 82 | 83 | // [[Rcpp::export]] 84 | SEXP test_double_it(RcppStdVector::std_vec_sxp& x) { 85 | for (auto s = x.begin(); s != x.end(); ++s) { 86 | switch(TYPEOF(*s)) { 87 | case REALSXP: dup_vec(*s); break; 88 | case INTSXP: dup_vec(*s); break; 89 | case LGLSXP: dup_vec(*s); break; 90 | case STRSXP: dup_vec(*s); break; 91 | case VECSXP: dup_vec(*s); break; 92 | default: Rcpp::stop("Unsupported column type"); 93 | } 94 | } 95 | UNPROTECT(x.size()); 96 | return Rcpp::wrap(x); 97 | } 98 | ``` 99 | The reason for calling ```PROTECT``` is because when the vector ```y``` goes out of scope, its internal ```SEXP``` will be unprotected. I get the following: 100 | ``` 101 | > test_double_it(list(1:3, letters[1:3], (1:3) > 2, pi)) 102 | [[1]] 103 | [1] 1 2 3 1 2 3 104 | 105 | [[2]] 106 | [1] "a" "b" "c" "a" "b" "c" 107 | 108 | [[3]] 109 | [1] FALSE FALSE TRUE FALSE FALSE TRUE 110 | 111 | [[4]] 112 | [1] 3.141593 3.141593 113 | ``` 114 | The following code demonstrates placement new construction of a ```std::vector``` over the memory owned by an R vector. 115 | ``` 116 | void test_inplace(RcppStdVector::std_ivec_int& x) { 117 | std::transform(std::begin(x), std::end(x), 118 | std::begin(x), [](int a){ return 2 * a; }); 119 | } 120 | ``` 121 | This function doubles each element of an R vector without ever allocating. 122 | ``` 123 | > x <- 1:10 124 | > test_inplace(x) 125 | > x 126 | [1] 2 4 6 8 10 12 14 16 18 20 127 | > 128 | ``` 129 | 130 | The basic idea of this experiment is to use the standard library components by adapting them to work on top of R's memory system. This is what I always wanted from Rcpp: a thin lightweight layer that enables use of standard C++. 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # RcppStdVector 5 | 6 | 7 | 8 | [![Lifecycle: 9 | experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) 10 | 11 | 12 | The `std::vector` container is the cornerstone of the C++ Standard 13 | Library. When used with Rcpp however, there is an inherent cost in using 14 | `std::vector` as memory must be copied both from and to R internal data 15 | structures. The RcppStdVector package allows users to write Rcpp code 16 | using the Standard Library `std::vector` container without incurring an 17 | extra container copy operation. Consider the following: 18 | 19 | std::vector 20 | my_func(std::vector& x) { // copy from REALSXP to std::vector here 21 | ... 22 | return x; // additional copy from std::vector to REALSXP here 23 | } 24 | 25 | For large vectors, this entails a large overhead. Rcpp native vectors 26 | avoid these copies by holding an `SEXP`, which will be only copied on 27 | demand. Recall however that unless an Rcpp vector is duplicated, 28 | functions can modify the vector in-place, which violates the 29 | no-side-effects rule. This can be useful or problematic in cases where 30 | the `SEXP` is aliased to several R variables. Most modifying Rcpp 31 | functions will therefore usually implement cloning the input vector. 32 | Similarly, using RcppStdVector, we can write: 33 | 34 | RcppStdVector::std_vec_real 35 | my_func(RcppStdVector::std_vec_real& x) { // copy here from REALSXP to std::vector 36 | ... 37 | return x; // zero-overhead, no-copy return of REALSXP 38 | } 39 | 40 | The zero-overhead return is achieved by using a custom allocator that 41 | allocates an R vector instead of general heap memory. This R vector is 42 | simply retrieved from the allocator at the end. Special consideration is 43 | taken when the capacity of the `std::vector` is greater than its size so 44 | that there are no dangling elements. It is furthermore possible to 45 | construct a standard vector in-place over an R vector via placement new. 46 | 47 | void my_func(RcppStandardVector::std_ivec_int& x) { // no copy 48 | // modify elements directly 49 | } 50 | 51 | The caveat to this is that any operation that causes allocation could 52 | lead to undefined behavior as the in-place allocator does not actually 53 | allocate and delete. Operations that grow the vector will throw an 54 | error. Provided overloads for assignment and copy construction will also 55 | throw. 56 | 57 | There are several reasons one might wish to use `std::vector` over the 58 | native Rcpp vector types. First, as part of the Standard Library, 59 | `std::vector` is simple and well-documented. Compare 60 | [this](https://en.cppreference.com/w/cpp/container/vector) to 61 | [this](http://dirk.eddelbuettel.com/code/rcpp/html/classRcpp_1_1Vector.html). 62 | Second, using `std::vector` everywhere minimizes code rewrites when 63 | moving between other C++ codebases and Rcpp. Third, `std::vector` 64 | supports amortized-constant appending elements. This is a very common 65 | idiom in C++ coding and its absense in Rcpp is a source of frustration. 66 | Consider the following functions: 67 | 68 | SEXP test_copy_chr(Rcpp::CharacterVector& x) { // no copy 69 | RcppStdVector::std_vec_chr y; // this IS a std::vector 70 | auto oi = std::back_inserter(y); // amortized-constant insertion 71 | std::copy(x.begin(), x.end(), oi); 72 | return Rcpp::wrap(y); // no copy 73 | } 74 | 75 | SEXP test_copy_rcpp_chr(Rcpp::CharacterVector& x) { // no copy 76 | Rcpp::CharacterVector y; // Rcpp type 77 | auto oi = std::back_inserter(y); // copies entire vector on each insertion 78 | std::copy(x.begin(), x.end(), oi); 79 | return Rcpp::wrap(y); // no copy 80 | } 81 | 82 | On my system, I 83 | get: 84 | 85 | > microbenchmark(r1 <- test_copy_chr(x), r2 <- test_copy_rcpp_chr(x)) 86 | Unit: microseconds 87 | expr min lq mean median uq max 88 | r1 <- test_copy_chr(x) 94.811 102.117 124.4515 125.6335 129.7775 218.154 89 | r2 <- test_copy_rcpp_chr(x) 935634.546 975367.489 1012093.6482 996092.4005 1023481.6770 1228233.548 90 | 91 | where `x` contains 1e4 character strings. The sole drawback of this 92 | idiom is that up to 50% of the memory allocated to the `std::vector` may 93 | be unused. 94 | 95 | RcppStdVector supports list and string-list types. For example the 96 | following copies a list of elements and duplicates each. 97 | 98 | template 99 | void dup_vec(SEXP& s) { 100 | auto y = RcppStdVector::from_sexp(s); 101 | auto oi = std::back_inserter(y); 102 | y.reserve(2 * y.size()); 103 | std::copy(RcppStdVector::begin(s), 104 | RcppStdVector::end(s), oi); 105 | s = PROTECT(Rcpp::wrap(y)); 106 | } 107 | 108 | // [[Rcpp::export]] 109 | SEXP test_double_it(RcppStdVector::std_vec_sxp& x) { 110 | for (auto s = x.begin(); s != x.end(); ++s) { 111 | switch(TYPEOF(*s)) { 112 | case REALSXP: dup_vec(*s); break; 113 | case INTSXP: dup_vec(*s); break; 114 | case LGLSXP: dup_vec(*s); break; 115 | case STRSXP: dup_vec(*s); break; 116 | case VECSXP: dup_vec(*s); break; 117 | default: Rcpp::stop("Unsupported column type"); 118 | } 119 | } 120 | UNPROTECT(x.size()); 121 | return Rcpp::wrap(x); 122 | } 123 | 124 | The reason for calling `PROTECT` is because when the vector `y` goes out 125 | of scope, its internal `SEXP` will be unprotected. I get the following: 126 | 127 | > test_double_it(list(1:3, letters[1:3], (1:3) > 2, pi)) 128 | [[1]] 129 | [1] 1 2 3 1 2 3 130 | 131 | [[2]] 132 | [1] "a" "b" "c" "a" "b" "c" 133 | 134 | [[3]] 135 | [1] FALSE FALSE TRUE FALSE FALSE TRUE 136 | 137 | [[4]] 138 | [1] 3.141593 3.141593 139 | 140 | The following code demonstrates placement new construction of a 141 | `std::vector` over the memory owned by an R vector. 142 | 143 | void test_inplace(RcppStdVector::std_ivec_int& x) { 144 | std::transform(std::begin(x), std::end(x), 145 | std::begin(x), [](int a){ return 2 * a; }); 146 | } 147 | 148 | This function doubles each element of an R vector without ever 149 | allocating. 150 | 151 | > x <- 1:10 152 | > test_inplace(x) 153 | > x 154 | [1] 2 4 6 8 10 12 14 16 18 20 155 | > 156 | 157 | The basic idea of this experiment is to use the standard library 158 | components by adapting them to work on top of R’s memory system. This is 159 | what I always wanted from Rcpp: a thin lightweight layer that enables 160 | use of standard C++. 161 | -------------------------------------------------------------------------------- /RcppStdVector.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /inst/include/RcppStdVector.h: -------------------------------------------------------------------------------- 1 | #ifndef __RCPPSTDVECTOR_H__ 2 | #define __RCPPSTDVECTOR_H__ 3 | 4 | #include 5 | 6 | namespace RcppStdVector { 7 | 8 | template struct ctype {}; 9 | template <> struct ctype { 10 | using type = double; 11 | }; 12 | template <> struct ctype { 13 | using type = int; 14 | }; 15 | template <> struct ctype { 16 | using type = int; 17 | }; 18 | template <> struct ctype { 19 | using type = SEXP; 20 | }; 21 | template <> struct ctype { 22 | using type = SEXP; 23 | }; 24 | 25 | template 26 | using value_t = typename ctype::type; 27 | 28 | template 29 | value_t* begin(SEXP x) { 30 | return static_cast*>(DATAPTR(x)); 31 | } 32 | 33 | template 34 | value_t* end(SEXP x) { 35 | return static_cast*>(DATAPTR(x)) + Rf_xlength(x); 36 | } 37 | 38 | template 39 | struct Rallocator { 40 | SEXP s; 41 | int count = 0; 42 | using value_type = value_t; 43 | value_type* get_ptr() { 44 | return begin(s); 45 | } 46 | value_type* allocate(std::size_t n) { 47 | s = Rf_allocVector(RTYPE, n); 48 | PROTECT(s); ++count; 49 | return get_ptr(); 50 | } 51 | void deallocate(value_type* p, std::size_t n) { 52 | if (count > 0) { 53 | UNPROTECT(count); 54 | count = 0; 55 | } 56 | } 57 | }; 58 | 59 | template 60 | bool operator==(Rallocator const& a1, Rallocator const& a2) { 61 | if (RTYPE1 != RTYPE2 || a1.get_ptr() != a2.get_ptr()) return false; 62 | return true; 63 | } 64 | 65 | template 66 | bool operator!=(Rallocator const& a1, Rallocator const& a2) { 67 | return !(a1 == a1); 68 | } 69 | 70 | template 71 | struct InPlaceAlloc { 72 | SEXP s; 73 | bool constructed = false; 74 | InPlaceAlloc(SEXP t) : s(t) {} 75 | using value_type = value_t; 76 | value_type* get_ptr() { 77 | return begin(s); 78 | } 79 | value_type* allocate(std::size_t n) { 80 | if (constructed) 81 | Rcpp::stop("In-place vectors cannot allocate"); 82 | constructed = true; 83 | return get_ptr(); 84 | } 85 | void deallocate(value_type* p, std::size_t n) {} 86 | void construct(value_type* p) {} 87 | }; 88 | 89 | template 90 | bool operator==(InPlaceAlloc const& a1, InPlaceAlloc const& a2) { 91 | if (RTYPE1 != RTYPE2 || a1.get_ptr() != a2.get_ptr()) return false; 92 | return true; 93 | } 94 | 95 | template 96 | bool operator!=(InPlaceAlloc const& a1, InPlaceAlloc const& a2) { 97 | return !(a1 == a1); 98 | } 99 | 100 | template 101 | using std_vec_t = std::vector, Rallocator>; 102 | 103 | using std_vec_real = std_vec_t; 104 | using std_vec_int = std_vec_t; 105 | using std_vec_lgl = std_vec_t; 106 | using std_vec_sxp = std_vec_t; 107 | using std_vec_chr = std_vec_t; 108 | 109 | template 110 | using std_ivec_t = std::vector, InPlaceAlloc>; 111 | 112 | using std_ivec_real = std_ivec_t; 113 | using std_ivec_int = std_ivec_t; 114 | using std_ivec_lgl = std_ivec_t; 115 | using std_ivec_sxp = std_ivec_t; 116 | using std_ivec_chr = std_ivec_t; 117 | 118 | template 119 | SEXP get_sexp(const T& x) { 120 | auto s = x.get_allocator().s; 121 | if (x.size() < x.capacity()) { 122 | SET_GROWABLE_BIT(s); 123 | SET_TRUELENGTH(s, x.capacity()); 124 | SETLENGTH(s, x.size()); 125 | } 126 | return s; 127 | } 128 | 129 | template 130 | std_vec_t 131 | from_sexp(SEXP s) { 132 | if (TYPEOF(s) != RTYPE) Rcpp::stop("Invalid type"); 133 | return std_vec_t(begin(s), end(s)); 134 | } 135 | 136 | template 137 | std_ivec_t 138 | from_sexp_inplace(SEXP s) { 139 | if (TYPEOF(s) != RTYPE) Rcpp::stop("Invalid type"); 140 | return std_ivec_t(Rf_xlength(s), InPlaceAlloc(s)); 141 | } 142 | 143 | }; // namespace RcppStdVector 144 | 145 | namespace Rcpp 146 | { 147 | 148 | #define gen_wrap_as(T, U) \ 149 | template<> \ 150 | inline SEXP \ 151 | wrap(const RcppStdVector::std_vec_##T& x) { \ 152 | return RcppStdVector::get_sexp(x); \ 153 | } \ 154 | template<> \ 155 | inline RcppStdVector::std_vec_##T \ 156 | as(SEXP s) { \ 157 | return RcppStdVector::from_sexp(s); \ 158 | } \ 159 | template<> \ 160 | inline SEXP \ 161 | wrap(const RcppStdVector::std_ivec_##T& x) { \ 162 | return RcppStdVector::get_sexp(x); \ 163 | } \ 164 | template<> \ 165 | inline RcppStdVector::std_ivec_##T \ 166 | as(SEXP s) { \ 167 | return RcppStdVector::from_sexp_inplace(s); \ 168 | } 169 | 170 | gen_wrap_as(real, REALSXP); 171 | gen_wrap_as(int, INTSXP); 172 | gen_wrap_as(lgl, LGLSXP); 173 | gen_wrap_as(chr, STRSXP); 174 | gen_wrap_as(sxp, VECSXP); 175 | 176 | }; // namespace Rcpp 177 | 178 | #include 179 | 180 | #endif // __RCPPSTDVECTOR_H__ 181 | -------------------------------------------------------------------------------- /man/figures/README-pressure-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thk686/RcppStdVector/7eadf6671df5dc9dd44d7b79f17346c3bf24f8eb/man/figures/README-pressure-1.png -------------------------------------------------------------------------------- /man/hello.Rd: -------------------------------------------------------------------------------- 1 | \name{hello} 2 | \alias{hello} 3 | \title{Hello, World!} 4 | \usage{ 5 | hello() 6 | } 7 | \description{ 8 | Prints 'Hello, world!'. 9 | } 10 | \examples{ 11 | hello() 12 | } 13 | -------------------------------------------------------------------------------- /man/rcpp_hello.Rd: -------------------------------------------------------------------------------- 1 | \name{rcpp_hello} 2 | \alias{rcpp_hello} 3 | \title{Hello, Rcpp!} 4 | \usage{ 5 | rcpp_hello() 6 | } 7 | \description{ 8 | Returns an \R \code{list} containing the character vector 9 | \code{c("foo", "bar")} and the numeric vector \code{c(0, 1)}. 10 | } 11 | \examples{ 12 | rcpp_hello() 13 | } 14 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | CXX_STD = CXX14 2 | PKG_CXXFLAGS = -I"../inst/include" 3 | -------------------------------------------------------------------------------- /src/RcppExports.cpp: -------------------------------------------------------------------------------- 1 | // Generated by using Rcpp::compileAttributes() -> do not edit by hand 2 | // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 3 | 4 | #include "../inst/include/RcppStdVector.h" 5 | #include 6 | 7 | using namespace Rcpp; 8 | 9 | // test_alloc 10 | SEXP test_alloc(); 11 | RcppExport SEXP _RcppStdVector_test_alloc() { 12 | BEGIN_RCPP 13 | Rcpp::RObject rcpp_result_gen; 14 | Rcpp::RNGScope rcpp_rngScope_gen; 15 | rcpp_result_gen = Rcpp::wrap(test_alloc()); 16 | return rcpp_result_gen; 17 | END_RCPP 18 | } 19 | // test_modify 20 | SEXP test_modify(RcppStdVector::std_vec_real& x); 21 | RcppExport SEXP _RcppStdVector_test_modify(SEXP xSEXP) { 22 | BEGIN_RCPP 23 | Rcpp::RObject rcpp_result_gen; 24 | Rcpp::RNGScope rcpp_rngScope_gen; 25 | Rcpp::traits::input_parameter< RcppStdVector::std_vec_real& >::type x(xSEXP); 26 | rcpp_result_gen = Rcpp::wrap(test_modify(x)); 27 | return rcpp_result_gen; 28 | END_RCPP 29 | } 30 | // test_list 31 | SEXP test_list(RcppStdVector::std_vec_sxp& x); 32 | RcppExport SEXP _RcppStdVector_test_list(SEXP xSEXP) { 33 | BEGIN_RCPP 34 | Rcpp::RObject rcpp_result_gen; 35 | Rcpp::RNGScope rcpp_rngScope_gen; 36 | Rcpp::traits::input_parameter< RcppStdVector::std_vec_sxp& >::type x(xSEXP); 37 | rcpp_result_gen = Rcpp::wrap(test_list(x)); 38 | return rcpp_result_gen; 39 | END_RCPP 40 | } 41 | // test_char 42 | SEXP test_char(); 43 | RcppExport SEXP _RcppStdVector_test_char() { 44 | BEGIN_RCPP 45 | Rcpp::RObject rcpp_result_gen; 46 | Rcpp::RNGScope rcpp_rngScope_gen; 47 | rcpp_result_gen = Rcpp::wrap(test_char()); 48 | return rcpp_result_gen; 49 | END_RCPP 50 | } 51 | // test_copy_chr 52 | SEXP test_copy_chr(Rcpp::CharacterVector& x); 53 | RcppExport SEXP _RcppStdVector_test_copy_chr(SEXP xSEXP) { 54 | BEGIN_RCPP 55 | Rcpp::RObject rcpp_result_gen; 56 | Rcpp::RNGScope rcpp_rngScope_gen; 57 | Rcpp::traits::input_parameter< Rcpp::CharacterVector& >::type x(xSEXP); 58 | rcpp_result_gen = Rcpp::wrap(test_copy_chr(x)); 59 | return rcpp_result_gen; 60 | END_RCPP 61 | } 62 | // test_copy_rcpp_chr 63 | SEXP test_copy_rcpp_chr(Rcpp::CharacterVector& x); 64 | RcppExport SEXP _RcppStdVector_test_copy_rcpp_chr(SEXP xSEXP) { 65 | BEGIN_RCPP 66 | Rcpp::RObject rcpp_result_gen; 67 | Rcpp::RNGScope rcpp_rngScope_gen; 68 | Rcpp::traits::input_parameter< Rcpp::CharacterVector& >::type x(xSEXP); 69 | rcpp_result_gen = Rcpp::wrap(test_copy_rcpp_chr(x)); 70 | return rcpp_result_gen; 71 | END_RCPP 72 | } 73 | // test_native 74 | RcppStdVector::std_vec_int test_native(RcppStdVector::std_vec_int& x); 75 | RcppExport SEXP _RcppStdVector_test_native(SEXP xSEXP) { 76 | BEGIN_RCPP 77 | Rcpp::RObject rcpp_result_gen; 78 | Rcpp::RNGScope rcpp_rngScope_gen; 79 | Rcpp::traits::input_parameter< RcppStdVector::std_vec_int& >::type x(xSEXP); 80 | rcpp_result_gen = Rcpp::wrap(test_native(x)); 81 | return rcpp_result_gen; 82 | END_RCPP 83 | } 84 | // test_double_it 85 | SEXP test_double_it(RcppStdVector::std_vec_sxp& x); 86 | RcppExport SEXP _RcppStdVector_test_double_it(SEXP xSEXP) { 87 | BEGIN_RCPP 88 | Rcpp::RObject rcpp_result_gen; 89 | Rcpp::RNGScope rcpp_rngScope_gen; 90 | Rcpp::traits::input_parameter< RcppStdVector::std_vec_sxp& >::type x(xSEXP); 91 | rcpp_result_gen = Rcpp::wrap(test_double_it(x)); 92 | return rcpp_result_gen; 93 | END_RCPP 94 | } 95 | // test_inplace 96 | void test_inplace(RcppStdVector::std_ivec_int& x); 97 | RcppExport SEXP _RcppStdVector_test_inplace(SEXP xSEXP) { 98 | BEGIN_RCPP 99 | Rcpp::RNGScope rcpp_rngScope_gen; 100 | Rcpp::traits::input_parameter< RcppStdVector::std_ivec_int& >::type x(xSEXP); 101 | test_inplace(x); 102 | return R_NilValue; 103 | END_RCPP 104 | } 105 | // test_copy_ivec 106 | void test_copy_ivec(RcppStdVector::std_ivec_int& x, RcppStdVector::std_ivec_int& y); 107 | RcppExport SEXP _RcppStdVector_test_copy_ivec(SEXP xSEXP, SEXP ySEXP) { 108 | BEGIN_RCPP 109 | Rcpp::RNGScope rcpp_rngScope_gen; 110 | Rcpp::traits::input_parameter< RcppStdVector::std_ivec_int& >::type x(xSEXP); 111 | Rcpp::traits::input_parameter< RcppStdVector::std_ivec_int& >::type y(ySEXP); 112 | test_copy_ivec(x, y); 113 | return R_NilValue; 114 | END_RCPP 115 | } 116 | // test_copy_const_ivec 117 | RcppStdVector::std_ivec_int test_copy_const_ivec(RcppStdVector::std_ivec_int& x); 118 | RcppExport SEXP _RcppStdVector_test_copy_const_ivec(SEXP xSEXP) { 119 | BEGIN_RCPP 120 | Rcpp::RObject rcpp_result_gen; 121 | Rcpp::RNGScope rcpp_rngScope_gen; 122 | Rcpp::traits::input_parameter< RcppStdVector::std_ivec_int& >::type x(xSEXP); 123 | rcpp_result_gen = Rcpp::wrap(test_copy_const_ivec(x)); 124 | return rcpp_result_gen; 125 | END_RCPP 126 | } 127 | // test_no_copy_construct_ivec 128 | void test_no_copy_construct_ivec(RcppStdVector::std_ivec_int& x); 129 | RcppExport SEXP _RcppStdVector_test_no_copy_construct_ivec(SEXP xSEXP) { 130 | BEGIN_RCPP 131 | Rcpp::RNGScope rcpp_rngScope_gen; 132 | Rcpp::traits::input_parameter< RcppStdVector::std_ivec_int& >::type x(xSEXP); 133 | test_no_copy_construct_ivec(x); 134 | return R_NilValue; 135 | END_RCPP 136 | } 137 | 138 | static const R_CallMethodDef CallEntries[] = { 139 | {"_RcppStdVector_test_alloc", (DL_FUNC) &_RcppStdVector_test_alloc, 0}, 140 | {"_RcppStdVector_test_modify", (DL_FUNC) &_RcppStdVector_test_modify, 1}, 141 | {"_RcppStdVector_test_list", (DL_FUNC) &_RcppStdVector_test_list, 1}, 142 | {"_RcppStdVector_test_char", (DL_FUNC) &_RcppStdVector_test_char, 0}, 143 | {"_RcppStdVector_test_copy_chr", (DL_FUNC) &_RcppStdVector_test_copy_chr, 1}, 144 | {"_RcppStdVector_test_copy_rcpp_chr", (DL_FUNC) &_RcppStdVector_test_copy_rcpp_chr, 1}, 145 | {"_RcppStdVector_test_native", (DL_FUNC) &_RcppStdVector_test_native, 1}, 146 | {"_RcppStdVector_test_double_it", (DL_FUNC) &_RcppStdVector_test_double_it, 1}, 147 | {"_RcppStdVector_test_inplace", (DL_FUNC) &_RcppStdVector_test_inplace, 1}, 148 | {"_RcppStdVector_test_copy_ivec", (DL_FUNC) &_RcppStdVector_test_copy_ivec, 2}, 149 | {"_RcppStdVector_test_copy_const_ivec", (DL_FUNC) &_RcppStdVector_test_copy_const_ivec, 1}, 150 | {"_RcppStdVector_test_no_copy_construct_ivec", (DL_FUNC) &_RcppStdVector_test_no_copy_construct_ivec, 1}, 151 | {NULL, NULL, 0} 152 | }; 153 | 154 | RcppExport void R_init_RcppStdVector(DllInfo *dll) { 155 | R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 156 | R_useDynamicSymbols(dll, FALSE); 157 | } 158 | -------------------------------------------------------------------------------- /src/RcppStdVector.cpp: -------------------------------------------------------------------------------- 1 | #include "RcppStdVector.h" 2 | 3 | #include "Rcpp.h" 4 | 5 | // [[Rcpp::export]] 6 | SEXP test_alloc() 7 | { 8 | RcppStdVector::std_vec_real x; 9 | for (int i = 0; i != 1000; ++i) x.push_back(i); 10 | return Rcpp::wrap(x); 11 | } 12 | 13 | // [[Rcpp::export]] 14 | SEXP test_modify(RcppStdVector::std_vec_real& x) 15 | { 16 | for (int i = 0; i != x.size(); ++i) 17 | x[i] = 2 * x[i]; 18 | return Rcpp::wrap(x); 19 | } 20 | 21 | // [[Rcpp::export]] 22 | SEXP test_list(RcppStdVector::std_vec_sxp& x) 23 | { 24 | x.push_back(Rf_allocVector(INTSXP, 10)); 25 | return Rcpp::wrap(x); 26 | } 27 | 28 | // [[Rcpp::export]] 29 | SEXP test_char() 30 | { 31 | RcppStdVector::std_vec_chr x; 32 | x.push_back(Rf_mkChar("test")); 33 | return Rcpp::wrap(x); 34 | } 35 | 36 | // [[Rcpp::export]] 37 | SEXP test_copy_chr(Rcpp::CharacterVector& x) { 38 | RcppStdVector::std_vec_chr y; 39 | auto oi = std::back_inserter(y); 40 | std::copy(x.begin(), x.end(), oi); 41 | return Rcpp::wrap(y); 42 | } 43 | 44 | // [[Rcpp::export]] 45 | SEXP test_copy_rcpp_chr(Rcpp::CharacterVector& x) { 46 | Rcpp::CharacterVector y; 47 | auto oi = std::back_inserter(y); 48 | std::copy(x.begin(), x.end(), oi); 49 | return Rcpp::wrap(y); 50 | } 51 | 52 | // [[Rcpp::export]] 53 | RcppStdVector::std_vec_int test_native(RcppStdVector::std_vec_int& x) { 54 | return x; 55 | } 56 | 57 | template 58 | void dup_vec(SEXP& s) { 59 | auto y = RcppStdVector::from_sexp(s); 60 | auto oi = std::back_inserter(y); 61 | y.reserve(2 * y.size()); 62 | std::copy(RcppStdVector::begin(s), 63 | RcppStdVector::end(s), oi); 64 | s = PROTECT(Rcpp::wrap(y)); 65 | } 66 | 67 | // [[Rcpp::export]] 68 | SEXP test_double_it(RcppStdVector::std_vec_sxp& x) { 69 | for (auto s = x.begin(); s != x.end(); ++s) { 70 | switch(TYPEOF(*s)) { 71 | case REALSXP: dup_vec(*s); break; 72 | case INTSXP: dup_vec(*s); break; 73 | case LGLSXP: dup_vec(*s); break; 74 | case STRSXP: dup_vec(*s); break; 75 | case VECSXP: dup_vec(*s); break; 76 | default: Rcpp::stop("Unsupported column type"); 77 | } 78 | } 79 | UNPROTECT(x.size()); 80 | return Rcpp::wrap(x); 81 | } 82 | 83 | // [[Rcpp::export]] 84 | void test_inplace(RcppStdVector::std_ivec_int& x) { 85 | std::transform(std::begin(x), std::end(x), 86 | std::begin(x), [](int a){ return 2 * a; }); 87 | } 88 | 89 | 90 | // [[Rcpp::export]] 91 | void test_copy_ivec(RcppStdVector::std_ivec_int& x, 92 | RcppStdVector::std_ivec_int& y) { 93 | x = y; 94 | } 95 | 96 | // [[Rcpp::export]] 97 | RcppStdVector::std_ivec_int 98 | test_copy_const_ivec(RcppStdVector::std_ivec_int& x) { 99 | RcppStdVector::std_ivec_int y(x); 100 | return y; 101 | } 102 | 103 | --------------------------------------------------------------------------------