Skip to content

Commit 7295915

Browse files
authored
Implement copy assignment for proxy (#391)
1 parent 8ebc7e8 commit 7295915

File tree

3 files changed

+103
-17
lines changed

3 files changed

+103
-17
lines changed

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# cpp11 (development version)
22

3+
* `cpp11::writable::r_vector<T>::proxy` now implements copy assignment.
4+
Practically this means that `x[i] = y[i]` now works when both `x` and `y`
5+
are writable vectors (#300, #339).
6+
37
* Implicit conversion from `sexp` to `bool`, `size_t`, and `double` has been
48
marked as deprecated and will be removed in the next version of cpp11. The 3
59
packages that were using this have been notified and sent PRs. The recommended

cpp11test/src/test-r_vector.cpp

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "cpp11/integers.hpp"
22
#include "cpp11/list.hpp"
33
#include "cpp11/protect.hpp"
4+
#include "cpp11/strings.hpp"
45

56
#include <testthat.h>
67

@@ -72,12 +73,8 @@ context("r_vector-capabilities-C++") {
7273
expect_true(std::is_trivially_destructible<integers::proxy>::value);
7374
expect_true(std::is_copy_constructible<integers::proxy>::value);
7475
expect_true(std::is_move_constructible<integers::proxy>::value);
75-
76-
// Should these be true? Does it affect anything in practice?
77-
expect_false(std::is_copy_assignable<integers::proxy>::value);
78-
expect_false(std::is_trivially_copy_assignable<integers::proxy>::value);
79-
expect_false(std::is_move_assignable<integers::proxy>::value);
80-
expect_false(std::is_trivially_move_assignable<integers::proxy>::value);
76+
expect_true(std::is_copy_assignable<integers::proxy>::value);
77+
expect_true(std::is_move_assignable<integers::proxy>::value);
8178
}
8279
}
8380
#endif
@@ -475,6 +472,66 @@ context("r_vector-C++") {
475472
UNPROTECT(2);
476473
}
477474

475+
test_that("`proxy` is copy assignable (integers) (#300, #339)") {
476+
cpp11::writable::integers foo = {1, 2, 3, 4, 5};
477+
cpp11::writable::integers bar = {6, 7, 8, 9, 10};
478+
479+
// Using rvalue temporaries (i.e. move assignable, but using copy assignment operator)
480+
for (R_xlen_t i = 0; i < foo.size(); ++i) {
481+
bar[i] = foo[i];
482+
}
483+
484+
// Using lvalues (i.e. copy assignable)
485+
cpp11::writable::integers::proxy x = foo[0];
486+
bar[4] = x;
487+
488+
expect_true(bar[0] == 1);
489+
expect_true(bar[1] == 2);
490+
expect_true(bar[2] == 3);
491+
expect_true(bar[3] == 4);
492+
expect_true(bar[4] == 1);
493+
}
494+
495+
test_that("`proxy` is copy assignable (list) (#300, #339)") {
496+
SEXP a = PROTECT(Rf_allocVector(INTSXP, 1));
497+
SEXP b = PROTECT(Rf_allocVector(REALSXP, 2));
498+
499+
cpp11::writable::list x({a, b});
500+
cpp11::writable::list y(2);
501+
502+
// Using rvalue temporaries (i.e. move assignable, but using copy assignment operator)
503+
y[0] = x[0];
504+
505+
// Using lvalues (i.e. copy assignable)
506+
cpp11::writable::list::proxy elt = x[1];
507+
y[1] = elt;
508+
509+
expect_true(y[0] == a);
510+
expect_true(y[1] == b);
511+
512+
UNPROTECT(2);
513+
}
514+
515+
test_that("`proxy` is copy assignable (strings) (#300, #339)") {
516+
SEXP a = PROTECT(Rf_mkCharCE("a", CE_UTF8));
517+
SEXP b = PROTECT(Rf_mkCharCE("b", CE_UTF8));
518+
519+
cpp11::writable::strings x({a, b});
520+
cpp11::writable::strings y(2);
521+
522+
// Using rvalue temporaries (i.e. move assignable, but using copy assignment operator)
523+
y[0] = x[0];
524+
525+
// Using lvalues (i.e. copy assignable)
526+
cpp11::writable::strings::proxy elt = x[1];
527+
y[1] = elt;
528+
529+
expect_true(y[0] == a);
530+
expect_true(y[1] == b);
531+
532+
UNPROTECT(2);
533+
}
534+
478535
test_that("std::max_element works on read only vectors") {
479536
SEXP foo_sexp = PROTECT(Rf_allocVector(INTSXP, 5));
480537
SET_INTEGER_ELT(foo_sexp, 0, 1);

inst/include/cpp11/r_vector.hpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ class r_vector : public cpp11::r_vector<T> {
272272
public:
273273
proxy(SEXP data, const R_xlen_t index, underlying_type* const p, bool is_altrep);
274274

275+
proxy& operator=(const proxy& rhs);
276+
275277
proxy& operator=(const T& rhs);
276278
proxy& operator+=(const T& rhs);
277279
proxy& operator-=(const T& rhs);
@@ -284,6 +286,10 @@ class r_vector : public cpp11::r_vector<T> {
284286
void operator--();
285287

286288
operator T() const;
289+
290+
private:
291+
underlying_type get() const;
292+
void set(underlying_type x);
287293
};
288294

289295
class iterator : public cpp11::r_vector<T>::const_iterator {
@@ -1176,16 +1182,16 @@ r_vector<T>::proxy::proxy(SEXP data, const R_xlen_t index,
11761182
: data_(data), index_(index), p_(p), is_altrep_(is_altrep) {}
11771183

11781184
template <typename T>
1179-
inline typename r_vector<T>::proxy& r_vector<T>::proxy::operator=(const T& rhs) {
1180-
underlying_type elt = static_cast<underlying_type>(rhs);
1181-
1182-
if (p_ != nullptr) {
1183-
*p_ = elt;
1184-
} else {
1185-
// Handles ALTREP, VECSXP, and STRSXP cases
1186-
set_elt(data_, index_, elt);
1187-
}
1185+
inline typename r_vector<T>::proxy& r_vector<T>::proxy::operator=(const proxy& rhs) {
1186+
const underlying_type elt = rhs.get();
1187+
set(elt);
1188+
return *this;
1189+
}
11881190

1191+
template <typename T>
1192+
inline typename r_vector<T>::proxy& r_vector<T>::proxy::operator=(const T& rhs) {
1193+
const underlying_type elt = static_cast<underlying_type>(rhs);
1194+
set(elt);
11891195
return *this;
11901196
}
11911197

@@ -1237,11 +1243,30 @@ inline void r_vector<T>::proxy::operator--() {
12371243

12381244
template <typename T>
12391245
inline r_vector<T>::proxy::operator T() const {
1240-
// Handles ALTREP, VECSXP, and STRSXP cases through `get_elt()`
1241-
const underlying_type elt = (p_ != nullptr) ? *p_ : r_vector::get_elt(data_, index_);
1246+
const underlying_type elt = get();
12421247
return static_cast<T>(elt);
12431248
}
12441249

1250+
template <typename T>
1251+
inline typename r_vector<T>::underlying_type r_vector<T>::proxy::get() const {
1252+
if (p_ != nullptr) {
1253+
return *p_;
1254+
} else {
1255+
// Handles ALTREP, VECSXP, and STRSXP cases
1256+
return r_vector::get_elt(data_, index_);
1257+
}
1258+
}
1259+
1260+
template <typename T>
1261+
inline void r_vector<T>::proxy::set(typename r_vector<T>::underlying_type x) {
1262+
if (p_ != nullptr) {
1263+
*p_ = x;
1264+
} else {
1265+
// Handles ALTREP, VECSXP, and STRSXP cases
1266+
set_elt(data_, index_, x);
1267+
}
1268+
}
1269+
12451270
template <typename T>
12461271
r_vector<T>::iterator::iterator(const r_vector* data, R_xlen_t pos)
12471272
: r_vector::const_iterator(data, pos) {}

0 commit comments

Comments
 (0)