#include "cpp11/integers.hpp"
#include "cpp11/list.hpp"
#include "cpp11/protect.hpp"
#include "cpp11/strings.hpp"
#include
#include // for max_element
context("r_vector-capabilities-C++") {
test_that("read only vector capabilities") {
using cpp11::integers;
expect_true(std::is_destructible::value);
expect_true(std::is_default_constructible::value);
expect_true(std::is_nothrow_default_constructible::value);
expect_true(std::is_copy_constructible::value);
expect_true(std::is_move_constructible::value);
expect_true(std::is_copy_assignable::value);
expect_true(std::is_move_assignable::value);
}
test_that("writable vector capabilities") {
using cpp11::writable::integers;
expect_true(std::is_destructible::value);
expect_true(std::is_default_constructible::value);
expect_true(std::is_nothrow_default_constructible::value);
expect_true(std::is_copy_constructible::value);
expect_true(std::is_move_constructible::value);
expect_true(std::is_copy_assignable::value);
expect_true(std::is_move_assignable::value);
}
test_that("read only const_iterator capabilities") {
using cpp11::integers;
expect_true(std::is_destructible::value);
expect_true(std::is_trivially_destructible::value);
expect_true(std::is_copy_constructible::value);
expect_true(std::is_move_constructible::value);
expect_true(std::is_copy_assignable::value);
expect_true(std::is_trivially_copy_assignable::value);
expect_true(std::is_move_assignable::value);
expect_true(std::is_trivially_move_assignable::value);
}
test_that("writable iterator capabilities") {
using cpp11::writable::integers;
expect_true(std::is_destructible::value);
expect_true(std::is_trivially_destructible::value);
expect_true(std::is_copy_constructible::value);
expect_true(std::is_move_constructible::value);
expect_true(std::is_copy_assignable::value);
expect_true(std::is_trivially_copy_assignable::value);
expect_true(std::is_move_assignable::value);
expect_true(std::is_trivially_move_assignable::value);
}
test_that("writable proxy capabilities") {
using cpp11::writable::integers;
expect_true(std::is_destructible::value);
expect_true(std::is_trivially_destructible::value);
expect_true(std::is_copy_constructible::value);
expect_true(std::is_move_constructible::value);
expect_true(std::is_copy_assignable::value);
expect_true(std::is_move_assignable::value);
}
}
context("r_vector-C++") {
test_that("writable vector temporary isn't leaked (integer) (#338)") {
R_xlen_t before = cpp11::detail::store::count();
// +1 from `x` allocation
cpp11::writable::integers x(1);
// Calls move assignment operator `operator=(r_vector&& rhs)`
// +1 from `rhs` allocation and move into `x`
// -1 from old `x` release
x = cpp11::writable::integers(1);
R_xlen_t after = cpp11::detail::store::count();
expect_true(before == 0);
expect_true(after - before == 1);
}
test_that("writable vector temporary isn't leaked (list) (#338)") {
R_xlen_t before = cpp11::detail::store::count();
// +1 from `x` allocation
cpp11::writable::list x(1);
// Calls move assignment operator `operator=(r_vector&& rhs)`
// +1 from `rhs` allocation and move into `x`
// -1 from old `x` release
x = cpp11::writable::list(1);
R_xlen_t after = cpp11::detail::store::count();
expect_true(before == 0);
expect_true(after - before == 1);
}
test_that("read-only vector copy constructor doesn't clear properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
SEXP x = PROTECT(Rf_allocVector(INTSXP, 1));
INTEGER(x)[0] = 1;
// +1 from `y` creation
cpp11::integers y(x);
// Calls read only copy constructor
// No duplication of `y`'s data is done
// +1 when adding `z`'s protection to the data
cpp11::integers z(y);
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have not been cleared
expect_true(y.data() != R_NilValue);
expect_true(y.size() == 1);
// `z` owns them now
expect_true(z.data() != R_NilValue);
expect_true(z.size() == 1);
// And these are the same! This is all read-only, so no need to duplicate
expect_true(z.data() == y.data());
expect_true(before == 0);
expect_true(after - before == 2);
UNPROTECT(1);
}
test_that("read-only vector move constructor clears properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
SEXP x = PROTECT(Rf_allocVector(INTSXP, 1));
INTEGER(x)[0] = 1;
// +1 from `y` creation
cpp11::integers y(x);
// Calls read only move constructor
// +0 when moving
cpp11::integers z(std::move(y));
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have been cleared in the move
expect_true(y.data() == R_NilValue);
expect_true(y.size() == 0);
// `z` owns them now
expect_true(z.data() != R_NilValue);
expect_true(z.size() == 1);
expect_true(before == 0);
expect_true(after - before == 1);
UNPROTECT(1);
}
test_that("writable vector copy constructor does not clear properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
// +1 from `y` allocation
cpp11::writable::integers y(1);
// Calls writable copy constructor
// +1 from duplicating `y` and protecting result
cpp11::writable::integers z(y);
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have not been cleared
expect_true(y.data() != R_NilValue);
expect_true(y.size() == 1);
// `z` is a duplicate of `y`
expect_true(z.data() != R_NilValue);
expect_true(z.size() == 1);
// And these are not the same! This is writable, so a duplication occurred.
expect_true(z.data() != y.data());
expect_true(before == 0);
expect_true(after - before == 2);
}
test_that("writable vector move constructor clears properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
// +1 from `y` allocation
cpp11::writable::integers y(1);
// Calls writable move constructor
// +0 when moving
cpp11::writable::integers z(std::move(y));
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have been cleared in the move
expect_true(y.data() == R_NilValue);
expect_true(y.size() == 0);
// `z` owns them now
expect_true(z.data() != R_NilValue);
expect_true(z.size() == 1);
expect_true(before == 0);
expect_true(after - before == 1);
}
test_that("read-only vector copy assignment operator doesn't clear properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
SEXP x = PROTECT(Rf_allocVector(INTSXP, 1));
INTEGER(x)[0] = 1;
// +1 from `y` creation
cpp11::integers y(x);
// +0 for default constructor
cpp11::integers z;
expect_true(z.data() == R_NilValue);
expect_true(z.size() == 0);
// Calls read only copy assignment operator `operator=(const r_vector& rhs)`
// +1 from additional protection of `y` (but not duplicating `y`'s data)
z = y;
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have not been cleared
expect_true(y.data() != R_NilValue);
expect_true(y.size() == 1);
// `z` properties have been updated
expect_true(z.data() != R_NilValue);
expect_true(z.size() == 1);
// And these are the same! This is all read-only, so no need to duplicate
expect_true(z.data() == y.data());
expect_true(before == 0);
expect_true(after - before == 2);
UNPROTECT(1);
}
test_that("read-only vector move assignment operator clears properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
SEXP x = PROTECT(Rf_allocVector(INTSXP, 1));
INTEGER(x)[0] = 1;
// +1 from `y` creation
cpp11::integers y(x);
// +0 for default constructor
cpp11::integers z;
// Calls read only move assignment operator `operator=(r_vector&& rhs)`
// +0 when moving
z = std::move(y);
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have been cleared in the move
expect_true(y.data() == R_NilValue);
expect_true(y.size() == 0);
expect_true(before == 0);
expect_true(after - before == 1);
UNPROTECT(1);
}
test_that("writable vector copy assignment operator doesn't clear properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
// +1 from `y` allocation
cpp11::writable::integers y(1);
// +0 for default constructor
cpp11::writable::integers z;
expect_true(z.data() == R_NilValue);
expect_true(z.size() == 0);
// Calls writable copy assignment operator `operator=(const r_vector& rhs)`
// +1 from protecting duplicate of `y`
z = y;
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have not been cleared
expect_true(y.data() != R_NilValue);
expect_true(y.size() == 1);
// `z` properties have been updated
expect_true(z.data() != R_NilValue);
expect_true(z.size() == 1);
// And these are not the same, we made a duplicate
expect_true(z.data() != y.data());
expect_true(before == 0);
expect_true(after - before == 2);
}
test_that("writable vector move assignment operator clears properties (#365)") {
R_xlen_t before = cpp11::detail::store::count();
// +1 from `y` allocation
cpp11::writable::integers y(1);
// +0 for default constructor
cpp11::writable::integers z;
// Calls writable move assignment operator `operator=(r_vector&& rhs)`
// +0 when moving (also clears `capacity` in this case)
z = std::move(y);
R_xlen_t after = cpp11::detail::store::count();
// `y` properties have been cleared in the move
expect_true(y.data() == R_NilValue);
expect_true(y.size() == 0);
expect_true(before == 0);
expect_true(after - before == 1);
}
test_that("writable vector copy assignment works with default constructed vectors") {
// Default constructed - the `data_` is `R_NilValue`!
cpp11::writable::integers x;
cpp11::writable::integers y(1);
// Checks that this guards against calling `INTEGER()` on `R_NilValue`.
y = x;
SEXP z(y);
expect_true(cpp11::detail::r_typeof(z) == INTSXP);
expect_true(Rf_xlength(z) == 0);
}
test_that("writable vector copy constructor correctly tracks the `capacity_`") {
cpp11::writable::integers x(2);
x[0] = 1;
x[1] = 2;
// Doubles the capacity from 2 to 4
x.push_back(3);
expect_true(Rf_xlength(x.data()) == 4);
// Calls writable copy constructor.
// Should duplicate without truncations and retain same capacity.
cpp11::writable::integers y(x);
expect_true(Rf_xlength(y.data()) == 4);
// In the past, we truncated (i.e. to size 3) but retained the same capacity of 4,
// so this could try to push without first resizing.
y.push_back(4);
expect_true(y[0] == 1);
expect_true(y[1] == 2);
expect_true(y[2] == 3);
expect_true(y[3] == 4);
}
test_that("writable vector copy constructor works with default constructed vectors") {
// Default constructed - the `data_` is `R_NilValue`!
cpp11::writable::integers x;
// Checks that this guards against calling `INTEGER()` on `R_NilValue`.
cpp11::writable::integers y(x);
SEXP z(y);
expect_true(cpp11::detail::r_typeof(z) == INTSXP);
expect_true(Rf_xlength(z) == 0);
}
test_that(
"read only vector copy constructor from a writable vector correctly truncates") {
cpp11::writable::integers x(2);
x[0] = 1;
x[1] = 2;
// Doubles the capacity from 2 to 4, meaning the underlying SEXP has length 4 now.
x.push_back(3);
expect_true(Rf_xlength(x.data()) == 4);
// Calls read only copy constructor from a writable vector.
// Should truncate the SEXP before wrapping in a read only vector.
cpp11::integers y(x);
expect_true(Rf_xlength(y.data()) == 3);
// `x` is still in a good state
expect_true(x.data() != R_NilValue);
expect_true(x.size() == 3);
// Even if we get a temporary writable vector, that goes through the same copy
// constructor as above, because we still have to truncate before taking ownership.
cpp11::integers z(std::move(x));
expect_true(Rf_xlength(z.data()) == 3);
// So technically `x` is still in a working state after this, although that is
// implementation defined and up to us to decide on
expect_true(x.data() != R_NilValue);
expect_true(x.size() == 3);
}
test_that(
"writable vector truncation resizes names and retains attributes (but not dim or "
"dim names)") {
cpp11::writable::integers x(2);
x[0] = 1;
x[1] = 2;
// Doubles the capacity from 2 to 4, meaning the underlying SEXP has length 4 now.
x.push_back(3);
expect_true(Rf_xlength(x.data()) == 4);
// Set some names
SEXP names = PROTECT(Rf_allocVector(STRSXP, 3));
SET_STRING_ELT(names, 0, Rf_mkCharCE("x", CE_UTF8));
SET_STRING_ELT(names, 1, Rf_mkCharCE("y", CE_UTF8));
SET_STRING_ELT(names, 2, Rf_mkCharCE("z", CE_UTF8));
x.names() = names;
// Length of names SEXP is actually 4 now, extended by `setAttrib()` to match
// the internal capacity
expect_true(Rf_xlength(Rf_getAttrib(x.data(), R_NamesSymbol)) == 4);
// Set an attribute
SEXP bar = PROTECT(Rf_ScalarInteger(1));
x.attr("foo") = bar;
// Extract out the underlying SEXP using the operator:
// - This truncates to size 3
// - This truncates and keeps names
// - This copies over attributes like `"foo"`
// - This updates the internal SEXP in `x` to the one in `x_sexp` (gross but users
// probably expect this at this point)
SEXP x_sexp = x;
expect_true(Rf_xlength(x_sexp) == 3);
expect_true(Rf_xlength(Rf_getAttrib(x_sexp, R_NamesSymbol)) == 3);
expect_true(Rf_getAttrib(x_sexp, Rf_install("foo")) == bar);
expect_true(x.data() == x_sexp);
UNPROTECT(2);
}
test_that("`proxy` is copy assignable (integers) (#300, #339)") {
cpp11::writable::integers foo = {1, 2, 3, 4, 5};
cpp11::writable::integers bar = {6, 7, 8, 9, 10};
// Using rvalue temporaries (i.e. move assignable, but using copy assignment operator)
for (R_xlen_t i = 0; i < foo.size(); ++i) {
bar[i] = foo[i];
}
// Using lvalues (i.e. copy assignable)
cpp11::writable::integers::proxy x = foo[0];
bar[4] = x;
expect_true(bar[0] == 1);
expect_true(bar[1] == 2);
expect_true(bar[2] == 3);
expect_true(bar[3] == 4);
expect_true(bar[4] == 1);
}
test_that("`proxy` is copy assignable (list) (#300, #339)") {
SEXP a = PROTECT(Rf_allocVector(INTSXP, 1));
SEXP b = PROTECT(Rf_allocVector(REALSXP, 2));
cpp11::writable::list x({a, b});
cpp11::writable::list y(2);
// Using rvalue temporaries (i.e. move assignable, but using copy assignment operator)
y[0] = x[0];
// Using lvalues (i.e. copy assignable)
cpp11::writable::list::proxy elt = x[1];
y[1] = elt;
expect_true(y[0] == a);
expect_true(y[1] == b);
UNPROTECT(2);
}
test_that("`proxy` is copy assignable (strings) (#300, #339)") {
SEXP a = PROTECT(Rf_mkCharCE("a", CE_UTF8));
SEXP b = PROTECT(Rf_mkCharCE("b", CE_UTF8));
cpp11::writable::strings x({a, b});
cpp11::writable::strings y(2);
// Using rvalue temporaries (i.e. move assignable, but using copy assignment operator)
y[0] = x[0];
// Using lvalues (i.e. copy assignable)
cpp11::writable::strings::proxy elt = x[1];
y[1] = elt;
expect_true(y[0] == a);
expect_true(y[1] == b);
UNPROTECT(2);
}
test_that("std::max_element works on read only vectors") {
SEXP foo_sexp = PROTECT(Rf_allocVector(INTSXP, 5));
SET_INTEGER_ELT(foo_sexp, 0, 1);
SET_INTEGER_ELT(foo_sexp, 1, 2);
SET_INTEGER_ELT(foo_sexp, 2, 5);
SET_INTEGER_ELT(foo_sexp, 3, 4);
SET_INTEGER_ELT(foo_sexp, 4, 3);
cpp11::integers foo(foo_sexp);
auto element = std::max_element(foo.begin(), foo.end());
expect_true(*element == 5);
UNPROTECT(1);
}
test_that("std::max_element works on writable vectors (#334)") {
cpp11::writable::integers foo = {1, 2, 5, 4, 3};
auto element = std::max_element(foo.begin(), foo.end());
expect_true(*element == 5);
}
}