Skip to content

Commit 6ddb13a

Browse files
authored
Unify initializer_list<named_arg> implementations (#382)
* Unify from `initializer_list<named_arg>` constructors * Use correct printf specifier * NEWS bullet * Include `Rversion.h` for `R_VERSION` * Make sure fallback for `SET_RAW_ELT()` is there * Use `r_typeof()` rather than `TYPEOF()` to get a consistent `SEXPTYPE`
1 parent 6df9fb5 commit 6ddb13a

17 files changed

+399
-183
lines changed

NEWS.md

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

3+
* Constructors for writable vectors from `initializer_list<named_arg>` now
4+
check that `named_arg` contains a length 1 object of the correct type, and
5+
throws either a `cpp11::type_error` or `std::length_error` if that is not the
6+
case (#382).
7+
38
* Repeated assignment to a `cpp11::writable::strings` vector through either
49
`x[i] = elt` or `x.push_back(elt)` is now more performant, at the tradeoff
510
of slightly less safety (as long as `elt` is actually a `CHARSXP` and `i` is

cpp11test/src/test-as.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include <string>
33
#include <vector>
44

5+
#include "cpp11/R.hpp"
56
#include "cpp11/declarations.hpp"
67

78
#include <testthat.h>
@@ -370,15 +371,15 @@ context("as_cpp-C++") {
370371
x.push_back(2);
371372

372373
auto res1 = cpp11::as_sexp(x);
373-
expect_true(TYPEOF(res1) == RAWSXP);
374+
expect_true(cpp11::detail::r_typeof(res1) == RAWSXP);
374375
expect_true(RAW(res1)[0] == 0);
375376
expect_true(RAW(res1)[1] == 1);
376377
expect_true(RAW(res1)[2] == 2);
377378

378379
cpp11::raws y(x);
379380

380381
auto res2 = cpp11::as_sexp(y);
381-
expect_true(TYPEOF(res2) == RAWSXP);
382+
expect_true(cpp11::detail::r_typeof(res2) == RAWSXP);
382383
expect_true(RAW(res2)[0] == 0);
383384
expect_true(RAW(res2)[1] == 1);
384385
expect_true(RAW(res2)[2] == 2);

cpp11test/src/test-doubles.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <cstring>
2+
#include "cpp11/R.hpp"
23
#include "cpp11/doubles.hpp"
34
#include "cpp11/function.hpp"
45
#include "cpp11/integers.hpp"
@@ -176,6 +177,47 @@ context("doubles-C++") {
176177
UNPROTECT(1);
177178
}
178179

180+
test_that("writable::doubles(initializer_list<named_arg>)") {
181+
using namespace cpp11::literals;
182+
183+
SEXP x1 = PROTECT(Rf_allocVector(REALSXP, 1));
184+
SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 1));
185+
SEXP x3 = PROTECT(Rf_allocVector(REALSXP, 1));
186+
187+
SET_REAL_ELT(x1, 0, 0.0);
188+
SET_REAL_ELT(x2, 0, 5.5);
189+
SET_REAL_ELT(x3, 0, NA_REAL);
190+
191+
// From scalar double vectors
192+
cpp11::writable::doubles x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
193+
expect_true(x.named());
194+
expect_true(x["one"] == 0.0);
195+
expect_true(x["two"] == 5.5);
196+
expect_true(R_IsNA(x["three"]));
197+
198+
// From doubles
199+
cpp11::writable::doubles y({"one"_nm = 0.0, "two"_nm = 5.5, "three"_nm = NA_REAL});
200+
expect_true(y.named());
201+
expect_true(y["one"] == 0.0);
202+
expect_true(y["two"] == 5.5);
203+
expect_true(R_IsNA(y["three"]));
204+
205+
UNPROTECT(3);
206+
}
207+
208+
test_that("writable::doubles(initializer_list<named_arg>) type check") {
209+
using namespace cpp11::literals;
210+
expect_error_as(cpp11::writable::doubles({"one"_nm = true}), cpp11::type_error);
211+
expect_error_as(cpp11::writable::doubles({"one"_nm = R_NilValue}), cpp11::type_error);
212+
}
213+
214+
test_that("writable::doubles(initializer_list<named_arg>) length check") {
215+
using namespace cpp11::literals;
216+
SEXP x = PROTECT(Rf_allocVector(REALSXP, 2));
217+
expect_error_as(cpp11::writable::doubles({"x"_nm = x}), std::length_error);
218+
UNPROTECT(1);
219+
}
220+
179221
test_that("writable::doubles(initializer_list<double>)") {
180222
cpp11::writable::doubles x({1, 2.5, 3});
181223
expect_true(x[0] == 1.0);
@@ -399,7 +441,7 @@ context("doubles-C++") {
399441
expect_true(i[1] == 13616);
400442
expect_true(i[2] == 124);
401443
expect_true(i[3] == 899);
402-
expect_true(TYPEOF(i) == REALSXP);
444+
expect_true(cpp11::detail::r_typeof(i) == REALSXP);
403445

404446
cpp11::writable::strings e;
405447
e.push_back("a");

cpp11test/src/test-integers.cpp

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "Rversion.h"
2+
#include "cpp11/R.hpp"
23
#include "cpp11/doubles.hpp"
34
#include "cpp11/function.hpp"
45
#include "cpp11/integers.hpp"
@@ -8,12 +9,12 @@
89

910
context("integers-C++") {
1011
test_that("as_integers(doubles)") {
11-
// TYPEOF(x) == INTSXP
12+
// r_typeof(x) == INTSXP
1213
cpp11::writable::doubles y;
1314
y.push_back(10.00);
1415
cpp11::writable::integers i(cpp11::as_integers(y));
1516
expect_true(i[0] == 10);
16-
expect_true(TYPEOF(i) == INTSXP);
17+
expect_true(cpp11::detail::r_typeof(i) == INTSXP);
1718

1819
cpp11::writable::doubles x;
1920
x.push_back(10.01);
@@ -35,7 +36,7 @@ context("integers-C++") {
3536
expect_true(t[1] == 1000);
3637
expect_true(t[2] == 100000);
3738
expect_true(t[3] == 100000);
38-
expect_true(TYPEOF(t) == INTSXP);
39+
expect_true(cpp11::detail::r_typeof(t) == INTSXP);
3940

4041
cpp11::writable::doubles na{NA_REAL, 42.};
4142
cpp11::integers na2(cpp11::as_integers(na));
@@ -168,6 +169,48 @@ context("integers-C++") {
168169
UNPROTECT(1);
169170
}
170171

172+
test_that("writable::integers(initializer_list<named_arg>)") {
173+
using namespace cpp11::literals;
174+
175+
SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1));
176+
SEXP x2 = PROTECT(Rf_allocVector(INTSXP, 1));
177+
SEXP x3 = PROTECT(Rf_allocVector(INTSXP, 1));
178+
179+
SET_INTEGER_ELT(x1, 0, 0);
180+
SET_INTEGER_ELT(x2, 0, 5);
181+
SET_INTEGER_ELT(x3, 0, NA_INTEGER);
182+
183+
// From scalar integer vectors
184+
cpp11::writable::integers x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
185+
expect_true(x.named());
186+
expect_true(x["one"] == 0);
187+
expect_true(x["two"] == 5);
188+
expect_true(x["three"] == NA_INTEGER);
189+
190+
// From ints
191+
cpp11::writable::integers y({"one"_nm = 0, "two"_nm = 5, "three"_nm = NA_INTEGER});
192+
expect_true(y.named());
193+
expect_true(y["one"] == 0);
194+
expect_true(y["two"] == 5);
195+
expect_true(y["three"] == NA_INTEGER);
196+
197+
UNPROTECT(3);
198+
}
199+
200+
test_that("writable::integers(initializer_list<named_arg>) type check") {
201+
using namespace cpp11::literals;
202+
expect_error_as(cpp11::writable::integers({"one"_nm = true}), cpp11::type_error);
203+
expect_error_as(cpp11::writable::integers({"one"_nm = R_NilValue}),
204+
cpp11::type_error);
205+
}
206+
207+
test_that("writable::integers(initializer_list<named_arg>) length check") {
208+
using namespace cpp11::literals;
209+
SEXP x = PROTECT(Rf_allocVector(INTSXP, 2));
210+
expect_error_as(cpp11::writable::integers({"x"_nm = x}), std::length_error);
211+
UNPROTECT(1);
212+
}
213+
171214
test_that("writable::integers(initializer_list<int>)") {
172215
cpp11::writable::integers x({1, 2, 3});
173216
expect_true(x[0] == 1);

cpp11test/src/test-list.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "cpp11/R.hpp"
12
#include "cpp11/doubles.hpp"
23
#include "cpp11/integers.hpp"
34
#include "cpp11/list.hpp"
@@ -49,7 +50,6 @@ context("list-C++") {
4950
expect_true(fifth[2] == 'c');
5051
}
5152

52-
using namespace cpp11::literals;
5353
test_that("unnamed_list.push_back(unnamed_arg)") {
5454
cpp11::writable::list x(1);
5555
x.push_back(cpp11::writable::integers(2));
@@ -58,6 +58,8 @@ context("list-C++") {
5858
}
5959

6060
test_that("unnamed_list.push_back(named_arg)") {
61+
using namespace cpp11::literals;
62+
6163
cpp11::writable::list x(1);
6264
x.push_back("bar"_nm = 2);
6365

@@ -69,6 +71,8 @@ context("list-C++") {
6971
}
7072

7173
test_that("named_list.push_back(unnamed_arg)") {
74+
using namespace cpp11::literals;
75+
7276
cpp11::writable::list x({"foo"_nm = 1});
7377
x.push_back(cpp11::writable::integers(2));
7478

@@ -80,6 +84,8 @@ context("list-C++") {
8084
}
8185

8286
test_that("named_list.push_back(named_arg)") {
87+
using namespace cpp11::literals;
88+
8389
cpp11::writable::list x({"foo"_nm = 1});
8490
x.push_back({"bar"_nm = 2});
8591

@@ -98,6 +104,8 @@ context("list-C++") {
98104
}
99105

100106
test_that("empty_list.push_back(named_arg)") {
107+
using namespace cpp11::literals;
108+
101109
cpp11::writable::list x;
102110
x.push_back({"bar"_nm = 2});
103111

@@ -126,6 +134,8 @@ context("list-C++") {
126134
}
127135

128136
test_that("list.named() works") {
137+
using namespace cpp11::literals;
138+
129139
cpp11::writable::list x({"bar"_nm = 2});
130140
expect_true(x.named());
131141

@@ -144,6 +154,8 @@ context("list-C++") {
144154
}
145155

146156
test_that("names of named lists are also resized") {
157+
using namespace cpp11::literals;
158+
147159
cpp11::writable::list x;
148160
x.push_back({"n1"_nm = 1});
149161
x.push_back({"n2"_nm = 2});
@@ -186,6 +198,30 @@ context("list-C++") {
186198
expect_true(y != R_NilValue);
187199
}
188200

201+
test_that("writable::list(initializer_list<named_arg>)") {
202+
using namespace cpp11::literals;
203+
204+
SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1));
205+
SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 2));
206+
SEXP x3 = PROTECT(Rf_allocVector(STRSXP, 3));
207+
208+
// Note that `x1`, `x2`, and `x3` are list elements, not lists of length 1!
209+
cpp11::writable::list x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
210+
expect_true(x.named());
211+
expect_true(x["one"] == x1);
212+
expect_true(x["two"] == x2);
213+
expect_true(x["three"] == x3);
214+
215+
// This also works, with varying types
216+
cpp11::writable::list y({"one"_nm = 1, "two"_nm = true, "three"_nm = 2.5});
217+
expect_true(y.named());
218+
expect_true(cpp11::detail::r_typeof(y["one"]) == INTSXP);
219+
expect_true(cpp11::detail::r_typeof(y["two"]) == LGLSXP);
220+
expect_true(cpp11::detail::r_typeof(y["three"]) == REALSXP);
221+
222+
UNPROTECT(3);
223+
}
224+
189225
test_that("writable::list(initializer_list<SEXP>)") {
190226
SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1));
191227
SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 2));

cpp11test/src/test-logicals.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,48 @@ context("logicals-C++") {
128128
UNPROTECT(1);
129129
}
130130

131+
test_that("writable::logicals(initializer_list<named_arg>)") {
132+
using namespace cpp11::literals;
133+
134+
SEXP x1 = PROTECT(Rf_allocVector(LGLSXP, 1));
135+
SEXP x2 = PROTECT(Rf_allocVector(LGLSXP, 1));
136+
SEXP x3 = PROTECT(Rf_allocVector(LGLSXP, 1));
137+
138+
SET_LOGICAL_ELT(x1, 0, 0);
139+
SET_LOGICAL_ELT(x2, 0, 1);
140+
SET_LOGICAL_ELT(x3, 0, NA_LOGICAL);
141+
142+
// From scalar logical vectors
143+
cpp11::writable::logicals x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
144+
expect_true(x.named());
145+
expect_true(x["one"] == cpp11::r_bool(false));
146+
expect_true(x["two"] == cpp11::r_bool(true));
147+
expect_true(x["three"] == cpp11::r_bool(NA_LOGICAL));
148+
149+
// From booleans
150+
cpp11::writable::logicals y({"one"_nm = true, "two"_nm = false, "three"_nm = true});
151+
expect_true(y.named());
152+
expect_true(y["one"] == cpp11::r_bool(true));
153+
expect_true(y["two"] == cpp11::r_bool(false));
154+
expect_true(y["three"] == cpp11::r_bool(true));
155+
156+
UNPROTECT(3);
157+
}
158+
159+
test_that("writable::logicals(initializer_list<named_arg>) type check") {
160+
using namespace cpp11::literals;
161+
expect_error_as(cpp11::writable::logicals({"one"_nm = 1}), cpp11::type_error);
162+
expect_error_as(cpp11::writable::logicals({"one"_nm = R_NilValue}),
163+
cpp11::type_error);
164+
}
165+
166+
test_that("writable::logicals(initializer_list<named_arg>) length check") {
167+
using namespace cpp11::literals;
168+
SEXP x = PROTECT(Rf_allocVector(LGLSXP, 2));
169+
expect_error_as(cpp11::writable::logicals({"x"_nm = x}), std::length_error);
170+
UNPROTECT(1);
171+
}
172+
131173
test_that("writable::logicals(initializer_list<r_bool>)") {
132174
cpp11::writable::logicals x(
133175
{cpp11::r_bool(true), cpp11::r_bool(false), cpp11::r_bool(NA_INTEGER)});

cpp11test/src/test-raws.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "cpp11/raws.hpp"
22

3+
#include "Rversion.h"
4+
35
#include <testthat.h>
46

57
context("raws-C++") {
@@ -128,6 +130,49 @@ context("raws-C++") {
128130
UNPROTECT(1);
129131
}
130132

133+
test_that("writable::raws(initializer_list<named_arg>)") {
134+
using namespace cpp11::literals;
135+
136+
SEXP x1 = PROTECT(Rf_allocVector(RAWSXP, 1));
137+
SEXP x2 = PROTECT(Rf_allocVector(RAWSXP, 1));
138+
SEXP x3 = PROTECT(Rf_allocVector(RAWSXP, 1));
139+
140+
#if R_VERSION >= R_Version(4, 2, 0)
141+
SET_RAW_ELT(x1, 0, 1);
142+
SET_RAW_ELT(x2, 0, 2);
143+
SET_RAW_ELT(x3, 0, 255);
144+
#else
145+
RAW(x1)[0] = 1;
146+
RAW(x2)[0] = 2;
147+
RAW(x3)[0] = 255;
148+
#endif
149+
150+
// From scalar raw vectors
151+
cpp11::writable::raws x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
152+
expect_true(x.named());
153+
expect_true(x["one"] == 1);
154+
expect_true(x["two"] == 2);
155+
expect_true(x["three"] == 255);
156+
157+
UNPROTECT(3);
158+
}
159+
160+
test_that("writable::raws(initializer_list<named_arg>) type check") {
161+
using namespace cpp11::literals;
162+
expect_error_as(cpp11::writable::raws({"one"_nm = true}), cpp11::type_error);
163+
expect_error_as(cpp11::writable::raws({"one"_nm = R_NilValue}), cpp11::type_error);
164+
165+
// `as_sexp()` turns this into an `INTSXP`, which is not compatible
166+
expect_error_as(cpp11::writable::raws({"one"_nm = 1}), cpp11::type_error);
167+
}
168+
169+
test_that("writable::raws(initializer_list<named_arg>) length check") {
170+
using namespace cpp11::literals;
171+
SEXP x = PROTECT(Rf_allocVector(RAWSXP, 2));
172+
expect_error_as(cpp11::writable::raws({"x"_nm = x}), std::length_error);
173+
UNPROTECT(1);
174+
}
175+
131176
test_that("writable::raws(initializer_list<uint_8>)") {
132177
cpp11::writable::raws x({1, 2, 255});
133178
expect_true(x[0] == 1);

0 commit comments

Comments
 (0)