Skip to content

Add r_bool wrapper for Rboolean #105

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 4 commits into from
Sep 23, 2020
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
4 changes: 2 additions & 2 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# cpp11 (development version)

* `r_bool` added as an adapter between `bool` and `Rboolean` values (#57, @bkietz)

* `data_frame()` objects now have the number of rows correctly set as real length, not the reserved length (#91)

* Fixed potential memory leak in cpp11::writable classes.
Expand Down Expand Up @@ -28,8 +30,6 @@

* `safe[]` can now work with functions that return any type (#70, @bkietz)

* `writable::logicals::operator=()` now allows C++ boolean values (#57, @romainfrancois)

* 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)
Expand Down
22 changes: 20 additions & 2 deletions cpp11test/src/test-as.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,6 @@ context("as_cpp-C++") {
test_that("as_sexp(bool)") {
SEXP l1 = PROTECT(cpp11::as_sexp(true));
SEXP l2 = PROTECT(cpp11::as_sexp(false));
/* TODO: handle NA_LOGICAL, possibly need a separate enum class rather than relying
* on bool or R's Rboolean */

expect_true(Rf_isLogical(l1));
expect_true(Rf_xlength(l1) == 1);
Expand All @@ -345,6 +343,26 @@ context("as_cpp-C++") {
UNPROTECT(2);
}

test_that("as_sexp(r_bool)") {
SEXP l1 = PROTECT(cpp11::as_sexp(cpp11::r_bool(true)));
SEXP l2 = PROTECT(cpp11::as_sexp(cpp11::r_bool(false)));
SEXP l3 = PROTECT(cpp11::as_sexp(cpp11::r_bool()));

expect_true(Rf_isLogical(l1));
expect_true(Rf_xlength(l1) == 1);
expect_true(LOGICAL(l1)[0] == TRUE);

expect_true(Rf_isLogical(l2));
expect_true(Rf_xlength(l2) == 1);
expect_true(LOGICAL(l2)[0] == FALSE);

expect_true(Rf_isLogical(l3));
expect_true(Rf_xlength(l3) == 1);
expect_true(LOGICAL(l3)[0] == NA_LOGICAL);

UNPROTECT(3);
}

test_that("as_sexp(raws)") {
cpp11::writable::raws x;
x.push_back(0);
Expand Down
12 changes: 8 additions & 4 deletions cpp11test/src/test-list.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
#include "cpp11/raws.hpp"
#include "cpp11/strings.hpp"

namespace cpp11 {
std::ostream& operator<<(std::ostream& os, r_bool b) { return os << int(b); }
} // namespace cpp11

context("list-C++") {
test_that("list.push_back()") {
cpp11::writable::list x;
Expand Down Expand Up @@ -36,10 +40,10 @@ context("list-C++") {
expect_true(third[0] == "foo");
expect_true(third[1] == "bar");

cpp11::logicals forth(x[3]);
expect_true(forth[0] == TRUE);
expect_true(forth[1] == FALSE);
expect_true(forth[2] == TRUE);
cpp11::logicals fourth(x[3]);
expect_true(fourth[0] == TRUE);
expect_true(fourth[1] == FALSE);
expect_true(fourth[2] == TRUE);

cpp11::raws fifth(x[4]);
expect_true(fifth[0] == 'a');
Expand Down
6 changes: 3 additions & 3 deletions cpp11test/src/test-logicals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,11 @@ context("logicals-C++") {

UNPROTECT(1);
}
test_that("is_na(Rboolean)") {
Rboolean x = TRUE;
test_that("is_na(r_bool)") {
cpp11::r_bool x = TRUE;
expect_true(!cpp11::is_na(x));

Rboolean y = NA_LOGICAL;
cpp11::r_bool y = NA_LOGICAL;
expect_true(cpp11::is_na(y));
}

Expand Down
1 change: 1 addition & 0 deletions inst/include/cpp11.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "cpp11/matrix.hpp"
#include "cpp11/named_arg.hpp"
#include "cpp11/protect.hpp"
#include "cpp11/r_bool.hpp"
#include "cpp11/r_string.hpp"
#include "cpp11/r_vector.hpp"
#include "cpp11/raws.hpp"
Expand Down
26 changes: 7 additions & 19 deletions inst/include/cpp11/R.hpp
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
#pragma once

#include <limits>

#include "R_ext/Arith.h"

#undef FALSE
#undef TRUE
#undef NA_LOGICAL

extern "C" {
typedef enum {
FALSE = 0,
TRUE = 1,
NA_LOGICAL = std::numeric_limits<int>::min()
} Rboolean;
}

#define R_EXT_BOOLEAN_H_
#ifdef R_INTERNALS_H_
#if !(defined(R_NO_REMAP) && defined(STRICT_R_HEADERS))
#error R headers were included before cpp11 headers \
and at least one of R_NO_REMAP or STRICT_R_HEADERS \
was not defined.
#endif
#endif

#define R_NO_REMAP
#define STRICT_R_HEADERS
#include "Rinternals.h"
#undef STRICT_R_HEADERS
#undef R_NO_REMAP

// clang-format off
#ifdef __clang__
Expand Down
3 changes: 2 additions & 1 deletion inst/include/cpp11/external_pointer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "cpp11/R.hpp" // for SEXP, SEXPREC, TYPEOF, R_NilValue, R_C...
#include "cpp11/protect.hpp" // for protect, safe, protect::function
#include "cpp11/r_bool.hpp" // for r_bool
#include "cpp11/r_vector.hpp" // for type_error
#include "cpp11/sexp.hpp" // for sexp

Expand Down Expand Up @@ -54,7 +55,7 @@ class external_pointer {
external_pointer(pointer p, bool use_deleter = true, bool finalize_on_exit = true)
: data_(safe[R_MakeExternalPtr]((void*)p, R_NilValue, R_NilValue)) {
if (use_deleter) {
R_RegisterCFinalizerEx(data_, r_deleter, static_cast<Rboolean>(finalize_on_exit));
R_RegisterCFinalizerEx(data_, r_deleter, static_cast<r_bool>(finalize_on_exit));
}
}

Expand Down
49 changes: 26 additions & 23 deletions inst/include/cpp11/logicals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
#include <array> // for array
#include <initializer_list> // for initializer_list

#include "cpp11/R.hpp" // for Rboolean, SEXP, SEXPREC, Rf_all...
#include "cpp11/R.hpp" // for SEXP, SEXPREC, Rf_all...
#include "cpp11/attribute_proxy.hpp" // for attribute_proxy
#include "cpp11/named_arg.hpp" // for named_arg
#include "cpp11/protect.hpp" // for preserved
#include "cpp11/r_bool.hpp" // for r_bool
#include "cpp11/r_vector.hpp" // for r_vector, r_vector<>::proxy
#include "cpp11/sexp.hpp" // for sexp

Expand All @@ -16,41 +17,41 @@
namespace cpp11 {

template <>
inline SEXP r_vector<Rboolean>::valid_type(SEXP data) {
inline SEXP r_vector<r_bool>::valid_type(SEXP data) {
if (TYPEOF(data) != LGLSXP) {
throw type_error(LGLSXP, TYPEOF(data));
}
return data;
}

template <>
inline Rboolean r_vector<Rboolean>::operator[](const R_xlen_t pos) const {
return is_altrep_ ? static_cast<Rboolean>(LOGICAL_ELT(data_, pos)) : data_p_[pos];
inline r_bool r_vector<r_bool>::operator[](const R_xlen_t pos) const {
return is_altrep_ ? static_cast<r_bool>(LOGICAL_ELT(data_, pos)) : data_p_[pos];
}

template <>
inline Rboolean* r_vector<Rboolean>::get_p(bool is_altrep, SEXP data) {
inline r_bool* r_vector<r_bool>::get_p(bool is_altrep, SEXP data) {
if (is_altrep) {
return nullptr;
} else {
return reinterpret_cast<Rboolean*>(LOGICAL(data));
return reinterpret_cast<r_bool*>(LOGICAL(data));
}
}

template <>
inline void r_vector<Rboolean>::const_iterator::fill_buf(R_xlen_t pos) {
inline void r_vector<r_bool>::const_iterator::fill_buf(R_xlen_t pos) {
length_ = std::min(64_xl, data_->size() - pos);
LOGICAL_GET_REGION(data_->data_, pos, length_, reinterpret_cast<int*>(buf_.data()));
block_start_ = pos;
}

typedef r_vector<Rboolean> logicals;
typedef r_vector<r_bool> logicals;

namespace writable {

template <>
inline typename r_vector<Rboolean>::proxy& r_vector<Rboolean>::proxy::operator=(
const Rboolean& rhs) {
inline typename r_vector<r_bool>::proxy& r_vector<r_bool>::proxy::operator=(
const r_bool& rhs) {
if (is_altrep_) {
SET_LOGICAL_ELT(data_, index_, rhs);
} else {
Expand All @@ -60,17 +61,21 @@ inline typename r_vector<Rboolean>::proxy& r_vector<Rboolean>::proxy::operator=(
}

template <>
inline r_vector<Rboolean>::proxy::operator Rboolean() const {
inline r_vector<r_bool>::proxy::operator r_bool() const {
if (p_ == nullptr) {
return static_cast<Rboolean>(LOGICAL_ELT(data_, index_));
return static_cast<r_bool>(LOGICAL_ELT(data_, index_));
} else {
return *p_;
}
}

inline bool operator==(const r_vector<r_bool>::proxy& lhs, r_bool rhs) {
return static_cast<r_bool>(lhs).operator==(rhs);
}

template <>
inline r_vector<Rboolean>::r_vector(std::initializer_list<Rboolean> il)
: cpp11::r_vector<Rboolean>(Rf_allocVector(LGLSXP, il.size())), capacity_(il.size()) {
inline r_vector<r_bool>::r_vector(std::initializer_list<r_bool> il)
: cpp11::r_vector<r_bool>(Rf_allocVector(LGLSXP, il.size())), capacity_(il.size()) {
protect_ = preserved.insert(data_);
auto it = il.begin();
for (R_xlen_t i = 0; i < capacity_; ++i, ++it) {
Expand All @@ -79,8 +84,8 @@ inline r_vector<Rboolean>::r_vector(std::initializer_list<Rboolean> il)
}

template <>
inline r_vector<Rboolean>::r_vector(std::initializer_list<named_arg> il)
: cpp11::r_vector<Rboolean>(safe[Rf_allocVector](LGLSXP, il.size())),
inline r_vector<r_bool>::r_vector(std::initializer_list<named_arg> il)
: cpp11::r_vector<r_bool>(safe[Rf_allocVector](LGLSXP, il.size())),
capacity_(il.size()) {
protect_ = preserved.insert(data_);
int n_protected = 0;
Expand All @@ -92,7 +97,7 @@ inline r_vector<Rboolean>::r_vector(std::initializer_list<named_arg> il)
++n_protected;
auto it = il.begin();
for (R_xlen_t i = 0; i < capacity_; ++i, ++it) {
data_p_[i] = static_cast<Rboolean>(LOGICAL_ELT(it->value(), 0));
data_p_[i] = static_cast<r_bool>(LOGICAL_ELT(it->value(), 0));
SET_STRING_ELT(names, i, Rf_mkCharCE(it->name(), CE_UTF8));
}
UNPROTECT(n_protected);
Expand All @@ -105,20 +110,20 @@ inline r_vector<Rboolean>::r_vector(std::initializer_list<named_arg> il)
}

template <>
inline void r_vector<Rboolean>::reserve(R_xlen_t new_capacity) {
inline void r_vector<r_bool>::reserve(R_xlen_t new_capacity) {
data_ = data_ == R_NilValue ? safe[Rf_allocVector](LGLSXP, new_capacity)
: safe[Rf_xlengthgets](data_, new_capacity);
SEXP old_protect = protect_;
protect_ = preserved.insert(data_);

preserved.release(old_protect);

data_p_ = reinterpret_cast<Rboolean*>(LOGICAL(data_));
data_p_ = reinterpret_cast<r_bool*>(LOGICAL(data_));
capacity_ = new_capacity;
}

template <>
inline void r_vector<Rboolean>::push_back(Rboolean value) {
inline void r_vector<r_bool>::push_back(r_bool value) {
while (length_ >= capacity_) {
reserve(capacity_ == 0 ? 1 : capacity_ *= 2);
}
Expand All @@ -130,9 +135,7 @@ inline void r_vector<Rboolean>::push_back(Rboolean value) {
++length_;
}

typedef r_vector<Rboolean> logicals;
typedef r_vector<r_bool> logicals;

} // namespace writable

inline bool is_na(Rboolean x) { return x == NA_LOGICAL; }
} // namespace cpp11
7 changes: 4 additions & 3 deletions inst/include/cpp11/matrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

#include <string> // for string

#include "cpp11/R.hpp" // for SEXP, SEXPREC, R_xlen_t, Rboolean, INT...
#include "cpp11/R.hpp" // for SEXP, SEXPREC, R_xlen_t, INT...
#include "cpp11/r_bool.hpp" // for r_bool
#include "cpp11/r_string.hpp" // for r_string
#include "cpp11/r_vector.hpp" // for r_vector
#include "cpp11/sexp.hpp" // for sexp
Expand Down Expand Up @@ -95,13 +96,13 @@ class matrix {

using doubles_matrix = matrix<r_vector<double>, double>;
using integers_matrix = matrix<r_vector<int>, int>;
using logicals_matrix = matrix<r_vector<Rboolean>, Rboolean>;
using logicals_matrix = matrix<r_vector<r_bool>, r_bool>;
using strings_matrix = matrix<r_vector<r_string>, r_string>;

namespace writable {
using doubles_matrix = matrix<r_vector<double>, r_vector<double>::proxy>;
using integers_matrix = matrix<r_vector<int>, r_vector<int>::proxy>;
using logicals_matrix = matrix<r_vector<Rboolean>, r_vector<Rboolean>::proxy>;
using logicals_matrix = matrix<r_vector<r_bool>, r_vector<r_bool>::proxy>;
using strings_matrix = matrix<r_vector<r_string>, r_vector<r_string>::proxy>;
} // namespace writable

Expand Down
13 changes: 7 additions & 6 deletions inst/include/cpp11/protect.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
#include <string> // for string, basic_string
#include <tuple> // for tuple, make_tuple

// NB: cpp11/R.hpp must precede R_ext includes so our definition of Rboolean is used
// NB: cpp11/R.hpp must precede R_ext/Error.h to ensure R_NO_REMAP is defined
#include "cpp11/R.hpp" // for SEXP, SEXPREC, CDR, R_NilValue, CAR, R_Pres...

#include "R_ext/Error.h" // for Rf_error, Rf_warning
#include "R_ext/Print.h" // for REprintf
#include "R_ext/Utils.h" // for R_CheckUserInterrupt
#include "Rversion.h" // for R_VERSION, R_Version
#include "R_ext/Boolean.h" // for Rboolean
#include "R_ext/Error.h" // for Rf_error, Rf_warning
#include "R_ext/Print.h" // for REprintf
#include "R_ext/Utils.h" // for R_CheckUserInterrupt
#include "Rversion.h" // for R_VERSION, R_Version

#if defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0)
#define HAS_UNWIND_PROTECT
Expand Down Expand Up @@ -51,7 +52,7 @@ SEXP unwind_protect(Fun&& code) {
},
&code,
[](void* jmpbuf, Rboolean jump) {
if (jump) {
if (jump == TRUE) {
// We need to first jump back into the C++ stacks because you can't safely throw
// exceptions from C stack frames.
longjmp(*static_cast<std::jmp_buf*>(jmpbuf), 1);
Expand Down
Loading