Skip to content

Unify initializer_list<named_arg> implementations #382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# cpp11 (development version)

* Constructors for writable vectors from `initializer_list<named_arg>` now
check that `named_arg` contains a length 1 object of the correct type, and
throws either a `cpp11::type_error` or `std::length_error` if that is not the
case (#382).

* Repeated assignment to a `cpp11::writable::strings` vector through either
`x[i] = elt` or `x.push_back(elt)` is now more performant, at the tradeoff
of slightly less safety (as long as `elt` is actually a `CHARSXP` and `i` is
Expand Down
5 changes: 3 additions & 2 deletions cpp11test/src/test-as.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <string>
#include <vector>

#include "cpp11/R.hpp"
#include "cpp11/declarations.hpp"

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

auto res1 = cpp11::as_sexp(x);
expect_true(TYPEOF(res1) == RAWSXP);
expect_true(cpp11::detail::r_typeof(res1) == RAWSXP);
expect_true(RAW(res1)[0] == 0);
expect_true(RAW(res1)[1] == 1);
expect_true(RAW(res1)[2] == 2);

cpp11::raws y(x);

auto res2 = cpp11::as_sexp(y);
expect_true(TYPEOF(res2) == RAWSXP);
expect_true(cpp11::detail::r_typeof(res2) == RAWSXP);
expect_true(RAW(res2)[0] == 0);
expect_true(RAW(res2)[1] == 1);
expect_true(RAW(res2)[2] == 2);
Expand Down
44 changes: 43 additions & 1 deletion cpp11test/src/test-doubles.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <cstring>
#include "cpp11/R.hpp"
#include "cpp11/doubles.hpp"
#include "cpp11/function.hpp"
#include "cpp11/integers.hpp"
Expand Down Expand Up @@ -176,6 +177,47 @@ context("doubles-C++") {
UNPROTECT(1);
}

test_that("writable::doubles(initializer_list<named_arg>)") {
using namespace cpp11::literals;

SEXP x1 = PROTECT(Rf_allocVector(REALSXP, 1));
SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 1));
SEXP x3 = PROTECT(Rf_allocVector(REALSXP, 1));

SET_REAL_ELT(x1, 0, 0.0);
SET_REAL_ELT(x2, 0, 5.5);
SET_REAL_ELT(x3, 0, NA_REAL);

// From scalar double vectors
cpp11::writable::doubles x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
expect_true(x.named());
expect_true(x["one"] == 0.0);
expect_true(x["two"] == 5.5);
expect_true(R_IsNA(x["three"]));

// From doubles
cpp11::writable::doubles y({"one"_nm = 0.0, "two"_nm = 5.5, "three"_nm = NA_REAL});
expect_true(y.named());
expect_true(y["one"] == 0.0);
expect_true(y["two"] == 5.5);
expect_true(R_IsNA(y["three"]));

UNPROTECT(3);
}

test_that("writable::doubles(initializer_list<named_arg>) type check") {
using namespace cpp11::literals;
expect_error_as(cpp11::writable::doubles({"one"_nm = true}), cpp11::type_error);
expect_error_as(cpp11::writable::doubles({"one"_nm = R_NilValue}), cpp11::type_error);
}

test_that("writable::doubles(initializer_list<named_arg>) length check") {
using namespace cpp11::literals;
SEXP x = PROTECT(Rf_allocVector(REALSXP, 2));
expect_error_as(cpp11::writable::doubles({"x"_nm = x}), std::length_error);
UNPROTECT(1);
}

test_that("writable::doubles(initializer_list<double>)") {
cpp11::writable::doubles x({1, 2.5, 3});
expect_true(x[0] == 1.0);
Expand Down Expand Up @@ -399,7 +441,7 @@ context("doubles-C++") {
expect_true(i[1] == 13616);
expect_true(i[2] == 124);
expect_true(i[3] == 899);
expect_true(TYPEOF(i) == REALSXP);
expect_true(cpp11::detail::r_typeof(i) == REALSXP);

cpp11::writable::strings e;
e.push_back("a");
Expand Down
49 changes: 46 additions & 3 deletions cpp11test/src/test-integers.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "Rversion.h"
#include "cpp11/R.hpp"
#include "cpp11/doubles.hpp"
#include "cpp11/function.hpp"
#include "cpp11/integers.hpp"
Expand All @@ -8,12 +9,12 @@

context("integers-C++") {
test_that("as_integers(doubles)") {
// TYPEOF(x) == INTSXP
// r_typeof(x) == INTSXP
cpp11::writable::doubles y;
y.push_back(10.00);
cpp11::writable::integers i(cpp11::as_integers(y));
expect_true(i[0] == 10);
expect_true(TYPEOF(i) == INTSXP);
expect_true(cpp11::detail::r_typeof(i) == INTSXP);

cpp11::writable::doubles x;
x.push_back(10.01);
Expand All @@ -35,7 +36,7 @@ context("integers-C++") {
expect_true(t[1] == 1000);
expect_true(t[2] == 100000);
expect_true(t[3] == 100000);
expect_true(TYPEOF(t) == INTSXP);
expect_true(cpp11::detail::r_typeof(t) == INTSXP);

cpp11::writable::doubles na{NA_REAL, 42.};
cpp11::integers na2(cpp11::as_integers(na));
Expand Down Expand Up @@ -168,6 +169,48 @@ context("integers-C++") {
UNPROTECT(1);
}

test_that("writable::integers(initializer_list<named_arg>)") {
using namespace cpp11::literals;

SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1));
SEXP x2 = PROTECT(Rf_allocVector(INTSXP, 1));
SEXP x3 = PROTECT(Rf_allocVector(INTSXP, 1));

SET_INTEGER_ELT(x1, 0, 0);
SET_INTEGER_ELT(x2, 0, 5);
SET_INTEGER_ELT(x3, 0, NA_INTEGER);

// From scalar integer vectors
cpp11::writable::integers x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
expect_true(x.named());
expect_true(x["one"] == 0);
expect_true(x["two"] == 5);
expect_true(x["three"] == NA_INTEGER);

// From ints
cpp11::writable::integers y({"one"_nm = 0, "two"_nm = 5, "three"_nm = NA_INTEGER});
expect_true(y.named());
expect_true(y["one"] == 0);
expect_true(y["two"] == 5);
expect_true(y["three"] == NA_INTEGER);

UNPROTECT(3);
}

test_that("writable::integers(initializer_list<named_arg>) type check") {
using namespace cpp11::literals;
expect_error_as(cpp11::writable::integers({"one"_nm = true}), cpp11::type_error);
expect_error_as(cpp11::writable::integers({"one"_nm = R_NilValue}),
cpp11::type_error);
}

test_that("writable::integers(initializer_list<named_arg>) length check") {
using namespace cpp11::literals;
SEXP x = PROTECT(Rf_allocVector(INTSXP, 2));
expect_error_as(cpp11::writable::integers({"x"_nm = x}), std::length_error);
UNPROTECT(1);
}

test_that("writable::integers(initializer_list<int>)") {
cpp11::writable::integers x({1, 2, 3});
expect_true(x[0] == 1);
Expand Down
38 changes: 37 additions & 1 deletion cpp11test/src/test-list.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "cpp11/R.hpp"
#include "cpp11/doubles.hpp"
#include "cpp11/integers.hpp"
#include "cpp11/list.hpp"
Expand Down Expand Up @@ -49,7 +50,6 @@ context("list-C++") {
expect_true(fifth[2] == 'c');
}

using namespace cpp11::literals;
test_that("unnamed_list.push_back(unnamed_arg)") {
cpp11::writable::list x(1);
x.push_back(cpp11::writable::integers(2));
Expand All @@ -58,6 +58,8 @@ context("list-C++") {
}

test_that("unnamed_list.push_back(named_arg)") {
using namespace cpp11::literals;

cpp11::writable::list x(1);
x.push_back("bar"_nm = 2);

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

test_that("named_list.push_back(unnamed_arg)") {
using namespace cpp11::literals;

cpp11::writable::list x({"foo"_nm = 1});
x.push_back(cpp11::writable::integers(2));

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

test_that("named_list.push_back(named_arg)") {
using namespace cpp11::literals;

cpp11::writable::list x({"foo"_nm = 1});
x.push_back({"bar"_nm = 2});

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

test_that("empty_list.push_back(named_arg)") {
using namespace cpp11::literals;

cpp11::writable::list x;
x.push_back({"bar"_nm = 2});

Expand Down Expand Up @@ -126,6 +134,8 @@ context("list-C++") {
}

test_that("list.named() works") {
using namespace cpp11::literals;

cpp11::writable::list x({"bar"_nm = 2});
expect_true(x.named());

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

test_that("names of named lists are also resized") {
using namespace cpp11::literals;

cpp11::writable::list x;
x.push_back({"n1"_nm = 1});
x.push_back({"n2"_nm = 2});
Expand All @@ -164,6 +176,30 @@ context("list-C++") {
expect_true(y != R_NilValue);
}

test_that("writable::list(initializer_list<named_arg>)") {
using namespace cpp11::literals;

SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1));
SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 2));
SEXP x3 = PROTECT(Rf_allocVector(STRSXP, 3));

// Note that `x1`, `x2`, and `x3` are list elements, not lists of length 1!
cpp11::writable::list x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
expect_true(x.named());
expect_true(x["one"] == x1);
expect_true(x["two"] == x2);
expect_true(x["three"] == x3);

// This also works, with varying types
cpp11::writable::list y({"one"_nm = 1, "two"_nm = true, "three"_nm = 2.5});
expect_true(y.named());
expect_true(cpp11::detail::r_typeof(y["one"]) == INTSXP);
expect_true(cpp11::detail::r_typeof(y["two"]) == LGLSXP);
expect_true(cpp11::detail::r_typeof(y["three"]) == REALSXP);

UNPROTECT(3);
}

test_that("writable::list(initializer_list<SEXP>)") {
SEXP x1 = PROTECT(Rf_allocVector(INTSXP, 1));
SEXP x2 = PROTECT(Rf_allocVector(REALSXP, 2));
Expand Down
42 changes: 42 additions & 0 deletions cpp11test/src/test-logicals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,48 @@ context("logicals-C++") {
UNPROTECT(1);
}

test_that("writable::logicals(initializer_list<named_arg>)") {
using namespace cpp11::literals;

SEXP x1 = PROTECT(Rf_allocVector(LGLSXP, 1));
SEXP x2 = PROTECT(Rf_allocVector(LGLSXP, 1));
SEXP x3 = PROTECT(Rf_allocVector(LGLSXP, 1));

SET_LOGICAL_ELT(x1, 0, 0);
SET_LOGICAL_ELT(x2, 0, 1);
SET_LOGICAL_ELT(x3, 0, NA_LOGICAL);

// From scalar logical vectors
cpp11::writable::logicals x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
expect_true(x.named());
expect_true(x["one"] == cpp11::r_bool(false));
expect_true(x["two"] == cpp11::r_bool(true));
expect_true(x["three"] == cpp11::r_bool(NA_LOGICAL));

// From booleans
cpp11::writable::logicals y({"one"_nm = true, "two"_nm = false, "three"_nm = true});
expect_true(y.named());
expect_true(y["one"] == cpp11::r_bool(true));
expect_true(y["two"] == cpp11::r_bool(false));
expect_true(y["three"] == cpp11::r_bool(true));

UNPROTECT(3);
}

test_that("writable::logicals(initializer_list<named_arg>) type check") {
using namespace cpp11::literals;
expect_error_as(cpp11::writable::logicals({"one"_nm = 1}), cpp11::type_error);
expect_error_as(cpp11::writable::logicals({"one"_nm = R_NilValue}),
cpp11::type_error);
}

test_that("writable::logicals(initializer_list<named_arg>) length check") {
using namespace cpp11::literals;
SEXP x = PROTECT(Rf_allocVector(LGLSXP, 2));
expect_error_as(cpp11::writable::logicals({"x"_nm = x}), std::length_error);
UNPROTECT(1);
}

test_that("writable::logicals(initializer_list<r_bool>)") {
cpp11::writable::logicals x(
{cpp11::r_bool(true), cpp11::r_bool(false), cpp11::r_bool(NA_INTEGER)});
Expand Down
45 changes: 45 additions & 0 deletions cpp11test/src/test-raws.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "cpp11/raws.hpp"

#include "Rversion.h"

#include <testthat.h>

context("raws-C++") {
Expand Down Expand Up @@ -128,6 +130,49 @@ context("raws-C++") {
UNPROTECT(1);
}

test_that("writable::raws(initializer_list<named_arg>)") {
using namespace cpp11::literals;

SEXP x1 = PROTECT(Rf_allocVector(RAWSXP, 1));
SEXP x2 = PROTECT(Rf_allocVector(RAWSXP, 1));
SEXP x3 = PROTECT(Rf_allocVector(RAWSXP, 1));

#if R_VERSION >= R_Version(4, 2, 0)
SET_RAW_ELT(x1, 0, 1);
SET_RAW_ELT(x2, 0, 2);
SET_RAW_ELT(x3, 0, 255);
#else
RAW(x1)[0] = 1;
RAW(x2)[0] = 2;
RAW(x3)[0] = 255;
#endif

// From scalar raw vectors
cpp11::writable::raws x({"one"_nm = x1, "two"_nm = x2, "three"_nm = x3});
expect_true(x.named());
expect_true(x["one"] == 1);
expect_true(x["two"] == 2);
expect_true(x["three"] == 255);

UNPROTECT(3);
}

test_that("writable::raws(initializer_list<named_arg>) type check") {
using namespace cpp11::literals;
expect_error_as(cpp11::writable::raws({"one"_nm = true}), cpp11::type_error);
expect_error_as(cpp11::writable::raws({"one"_nm = R_NilValue}), cpp11::type_error);

// `as_sexp()` turns this into an `INTSXP`, which is not compatible
expect_error_as(cpp11::writable::raws({"one"_nm = 1}), cpp11::type_error);
}

test_that("writable::raws(initializer_list<named_arg>) length check") {
using namespace cpp11::literals;
SEXP x = PROTECT(Rf_allocVector(RAWSXP, 2));
expect_error_as(cpp11::writable::raws({"x"_nm = x}), std::length_error);
UNPROTECT(1);
}

test_that("writable::raws(initializer_list<uint_8>)") {
cpp11::writable::raws x({1, 2, 255});
expect_true(x[0] == 1);
Expand Down
Loading
Loading