From eb13087219591542ccbe78a28ef834b51fbfbc8b Mon Sep 17 00:00:00 2001 From: Romain Francois Date: Thu, 23 Jul 2020 13:55:26 +0200 Subject: [PATCH 1/2] as_cpp(LGLSXP scalar) --- inst/include/cpp11/as.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/inst/include/cpp11/as.hpp b/inst/include/cpp11/as.hpp index 74b56434..4840973a 100644 --- a/inst/include/cpp11/as.hpp +++ b/inst/include/cpp11/as.hpp @@ -55,6 +55,10 @@ is_integral as_cpp(SEXP from) { return value; } } + } else if (Rf_isLogical(from)) { + if (Rf_xlength(from) == 1) { + return LOGICAL_ELT(from, 0); + } } stop("Expected single integer value"); From 17d488c802ab35cbd52d59a98605e9512f3f57eb Mon Sep 17 00:00:00 2001 From: Jim Hester Date: Tue, 28 Jul 2020 08:23:43 -0400 Subject: [PATCH 2/2] Handle all three NA types in as_cpp and as_cpp --- NEWS.md | 2 ++ cpp11test/src/test-as.cpp | 43 ++++++++++++++++++++++++++++++++++++++- inst/include/cpp11/as.hpp | 22 ++++++++++++++++++-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1e6389ed..7ccca3cb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # cpp11 (development version) +* `cpp11::as_cpp()` and `cpp11::as_cpp()` now implicitly coerce between all 3 types of single NA values (#53). + * The `END_CPP` macro now includes a `catch(...)` block to catch all C++ exceptions that do not inherit from `std::exception` (#47). * Improve consistency of inserting NA values in r_string objects (#45) diff --git a/cpp11test/src/test-as.cpp b/cpp11test/src/test-as.cpp index 855ea8a9..33f2ac84 100644 --- a/cpp11test/src/test-as.cpp +++ b/cpp11test/src/test-as.cpp @@ -55,6 +55,26 @@ context("as_cpp-C++") { UNPROTECT(1); } + test_that("as_cpp(NA)") { + SEXP r = PROTECT(Rf_allocVector(REALSXP, 1)); + SEXP i = PROTECT(Rf_allocVector(INTSXP, 1)); + SEXP l = PROTECT(Rf_allocVector(LGLSXP, 1)); + REAL(r)[0] = NA_REAL; + INTEGER(i)[0] = NA_INTEGER; + LOGICAL(l)[0] = NA_LOGICAL; + + auto x1 = cpp11::as_cpp(r); + expect_true(x1 == NA_INTEGER); + + auto x2 = cpp11::as_cpp(i); + expect_true(x2 == NA_INTEGER); + + auto x3 = cpp11::as_cpp(l); + expect_true(x3 == NA_INTEGER); + + UNPROTECT(3); + } + test_that("as_cpp(REALSXP)") { SEXP r = PROTECT(Rf_allocVector(REALSXP, 1)); REAL(r)[0] = 1.2; @@ -85,6 +105,26 @@ context("as_cpp-C++") { UNPROTECT(1); } + test_that("as_cpp(NA)") { + SEXP r = PROTECT(Rf_allocVector(REALSXP, 1)); + SEXP i = PROTECT(Rf_allocVector(INTSXP, 1)); + SEXP l = PROTECT(Rf_allocVector(LGLSXP, 1)); + REAL(r)[0] = NA_REAL; + INTEGER(i)[0] = NA_INTEGER; + LOGICAL(l)[0] = NA_LOGICAL; + + auto x1 = cpp11::as_cpp(r); + expect_true(ISNA(x1)); + + auto x2 = cpp11::as_cpp(i); + expect_true(ISNA(x2)); + + auto x3 = cpp11::as_cpp(l); + expect_true(ISNA(x3)); + + UNPROTECT(3); + } + test_that("as_cpp()") { SEXP r = PROTECT(Rf_allocVector(LGLSXP, 1)); LOGICAL(r)[0] = TRUE; @@ -382,7 +422,8 @@ context("as_cpp-C++") { } test_that("as_sexp(r_vector)") { - SEXP s1 = PROTECT(cpp11::as_sexp(std::vector({"foo", "bar", "baz"}))); + SEXP s1 = + PROTECT(cpp11::as_sexp(std::vector({"foo", "bar", "baz"}))); expect_true(Rf_isString(s1)); expect_true(Rf_xlength(s1) == 3); diff --git a/inst/include/cpp11/as.hpp b/inst/include/cpp11/as.hpp index 4840973a..3efd7aa1 100644 --- a/inst/include/cpp11/as.hpp +++ b/inst/include/cpp11/as.hpp @@ -50,6 +50,9 @@ is_integral as_cpp(SEXP from) { } } else if (Rf_isReal(from)) { if (Rf_xlength(from) == 1) { + if (ISNA(REAL_ELT(from, 0))) { + return NA_INTEGER; + } double value = REAL_ELT(from, 0); if (is_convertable_without_loss_to_integer(value)) { return value; @@ -57,7 +60,9 @@ is_integral as_cpp(SEXP from) { } } else if (Rf_isLogical(from)) { if (Rf_xlength(from) == 1) { - return LOGICAL_ELT(from, 0); + if (LOGICAL_ELT(from, 0) == NA_LOGICAL) { + return NA_INTEGER; + } } } @@ -94,12 +99,25 @@ is_floating_point_value as_cpp(SEXP from) { return REAL_ELT(from, 0); } } - // All integers can be coerced to doubles, so we just convert them. + // All 32 bit integers can be coerced to doubles, so we just convert them. if (Rf_isInteger(from)) { if (Rf_xlength(from) == 1) { + if (INTEGER_ELT(from, 0) == NA_INTEGER) { + return NA_REAL; + } return INTEGER_ELT(from, 0); } } + + // Also allow NA values + if (Rf_isLogical(from)) { + if (Rf_xlength(from) == 1) { + if (LOGICAL_ELT(from, 0) == NA_LOGICAL) { + return NA_REAL; + } + } + } + stop("Expected single double value"); return T();