Skip to content

Commit 036aec7

Browse files
committed
fix copy constructors for C++ 11/14
1 parent 3368d63 commit 036aec7

34 files changed

+813
-36
lines changed

Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ check:
2121
@$(MAKE) install
2222
@$(MAKE) register
2323
@Rscript -e 'devtools::check("./", error_on = "error")'
24-
@Rscript -e 'cpp4r::register("extended-tests/cpp4rtest")'
25-
@Rscript -e 'devtools::check("./", error_on = "error")'
2624
@echo "==============================="
2725
@echo "Checking C++ code"
2826
@$(MAKE) install
2927
@rm -f extended-tests-results/*.log
3028
@rm -f extended-tests-results/check-results.md
29+
@Rscript -e 'cpp4r::register("extended-tests/cpp4rtest")'
3130
@export -p USE_CLANG; /bin/bash -euo pipefail -c './scripts/check_loop.sh'
3231
@echo "==============================="
3332

example-packages/cpp4rgaussjordan/src/vendor/cpp4r/complexes.hpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,7 @@ class r_vector<r_complex>::proxy {
221221
// New complexes_vector class for handling complex numbers in SEXP
222222
class complexes_vector {
223223
public:
224-
explicit complexes_vector(SEXP x) noexcept
225-
: data_(reinterpret_cast<Rcomplex*>(DATAPTR(x))), size_(Rf_length(x)) {}
224+
explicit complexes_vector(SEXP x) noexcept : data_(COMPLEX(x)), size_(Rf_length(x)) {}
226225

227226
std::complex<double> operator[](R_xlen_t i) const noexcept {
228227
return {data_[i].r, data_[i].i};

example-packages/cpp4rgaussjordan/src/vendor/cpp4r/list.hpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,18 @@ template <>
3535
inline typename r_vector<SEXP>::underlying_type const* r_vector<SEXP>::get_const_p(
3636
bool is_altrep, SEXP data) noexcept {
3737
// No `VECTOR_PTR_OR_NULL()`
38-
return __builtin_expect(is_altrep, 0) ? nullptr
39-
: static_cast<SEXP const*>(DATAPTR_RO(data));
38+
if (is_altrep) {
39+
return nullptr;
40+
} else {
41+
#if defined(R_VERSION) && R_VERSION >= R_Version(4, 5, 0)
42+
// R 4.5.0+ provides VECTOR_PTR_RO as part of the public API
43+
return VECTOR_PTR_RO(data);
44+
#else
45+
// For older R versions, return nullptr and rely on VECTOR_ELT for element access
46+
// DATAPTR_RO is not part of the public API and causes CRAN check failures
47+
return nullptr;
48+
#endif
49+
}
4050
}
4151

4252
/// Specialization for lists, where `x["oob"]` returns `R_NilValue`, like at the R level

extended-tests/cpp4rtest/configure

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,11 @@ SHLIB_OPENMP_CXXFLAGS=$(${R_HOME}/bin/R CMD config SHLIB_OPENMP_CXXFLAGS 2>/dev/
88
BLAS_LIBS=$(${R_HOME}/bin/R CMD config BLAS_LIBS)
99
LAPACK_LIBS=$(${R_HOME}/bin/R CMD config LAPACK_LIBS)
1010

11+
# If CXX_STD is empty, default to CXX11 (minimum required by cpp4r)
12+
if [ -z "${CXX_STD:-}" ]; then
13+
echo "configure: CXX_STD not set, defaulting to CXX11"
14+
CXX_STD="CXX11"
15+
fi
16+
1117
sed -e "s|@CXX_STD@|${CXX_STD}|g" \
1218
src/Makevars.in > src/Makevars

extended-tests/cpp4rtest/src/main.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,36 @@ using namespace cpp4r;
3030

3131
// Test headers (these contain testthat tests, not exported functions)
3232
#include "test-as.h"
33+
#include "test-as_advanced.h"
34+
#include "test-as_complexes.h"
35+
#include "test-attribute_proxy.h"
3336
#include "test-complex.h"
3437
#include "test-data_frame.h"
3538
#include "test-doubles.h"
3639
#include "test-environment.h"
40+
#include "test-environment_advanced.h"
3741
#include "test-external_pointer.h"
3842
#include "test-function.h"
43+
#include "test-function_advanced.h"
3944
#include "test-integers.h"
4045
#include "test-list.h"
4146
#include "test-list_of.h"
4247
#include "test-logicals.h"
4348
#include "test-matrix.h"
49+
#include "test-matrix_advanced.h"
50+
#include "test-named_arg.h"
4451
#include "test-nas.h"
4552
#include "test-protect-nested.h"
4653
#include "test-protect.h"
54+
#include "test-protect_advanced.h"
55+
#include "test-r_bool_advanced.h"
4756
#include "test-r_complex.h"
57+
#include "test-r_string_advanced.h"
4858
#include "test-r_vector.h"
59+
#include "test-r_vector_advanced.h"
4960
#include "test-raws.h"
5061
#include "test-sexp.h"
62+
#include "test-sexp_advanced.h"
5163
#include "test-string.h"
5264
#include "test-strings.h"
65+
#include "test-translate_names.h"
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <testthat.h>
2+
3+
#include <deque>
4+
#include <list>
5+
#include <map>
6+
#include <unordered_map>
7+
8+
context("as_advanced-C++") {
9+
test_that("as_cpp with int type") {
10+
SEXP x = PROTECT(Rf_ScalarInteger(42));
11+
auto val = cpp4r::as_cpp<int>(x);
12+
expect_true(val == 42);
13+
UNPROTECT(1);
14+
}
15+
16+
test_that("as_sexp with std::map<std::string, SEXP>") {
17+
std::map<std::string, SEXP> m;
18+
m["a"] = PROTECT(Rf_ScalarInteger(1));
19+
m["b"] = PROTECT(Rf_ScalarInteger(2));
20+
21+
SEXP result = PROTECT(cpp4r::as_sexp(m));
22+
expect_true(Rf_isVector(result));
23+
expect_true(Rf_xlength(result) == 2);
24+
25+
SEXP names = Rf_getAttrib(result, R_NamesSymbol);
26+
expect_true(names != R_NilValue);
27+
UNPROTECT(3);
28+
}
29+
30+
test_that("as_sexp with std::map<double, int>") {
31+
std::map<double, int> m;
32+
m[1.5] = 10;
33+
m[2.5] = 20;
34+
35+
SEXP result = PROTECT(cpp4r::as_sexp(m));
36+
expect_true(Rf_isVector(result));
37+
UNPROTECT(1);
38+
}
39+
40+
test_that("as_sexp with std::unordered_map<std::string, SEXP>") {
41+
std::unordered_map<std::string, SEXP> m;
42+
m["x"] = PROTECT(Rf_ScalarReal(1.5));
43+
m["y"] = PROTECT(Rf_ScalarReal(2.5));
44+
45+
SEXP result = PROTECT(cpp4r::as_sexp(m));
46+
expect_true(Rf_isVector(result));
47+
UNPROTECT(3);
48+
}
49+
50+
test_that("as_sexp with std::list<double>") {
51+
std::list<double> l = {1.0, 2.0, 3.0};
52+
SEXP result = PROTECT(cpp4r::as_sexp(l));
53+
expect_true(Rf_isReal(result));
54+
expect_true(Rf_xlength(result) == 3);
55+
UNPROTECT(1);
56+
}
57+
58+
test_that("as_cpp with SEXP passthrough") {
59+
SEXP x = PROTECT(Rf_ScalarInteger(42));
60+
SEXP y = cpp4r::as_cpp<SEXP>(x);
61+
expect_true(x == y);
62+
UNPROTECT(1);
63+
}
64+
65+
test_that("as_cpp for std::complex<double>") {
66+
SEXP x = PROTECT(Rf_allocVector(CPLXSXP, 1));
67+
COMPLEX(x)[0].r = 1.0;
68+
COMPLEX(x)[0].i = 2.0;
69+
70+
std::complex<double> c = cpp4r::as_cpp(x);
71+
expect_true(c.real() == 1.0);
72+
expect_true(c.imag() == 2.0);
73+
UNPROTECT(1);
74+
}
75+
76+
test_that("as_sexp for std::complex<double>") {
77+
std::complex<double> c(3.0, 4.0);
78+
SEXP result = PROTECT(cpp4r::as_sexp(c));
79+
expect_true(Rf_isComplex(result));
80+
expect_true(COMPLEX(result)[0].r == 3.0);
81+
expect_true(COMPLEX(result)[0].i == 4.0);
82+
UNPROTECT(1);
83+
}
84+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <testthat.h>
2+
3+
context("as_complexes-C++") {
4+
test_that("as_complexes from integers") {
5+
cpp4r::writable::integers x({1, 2, 3});
6+
cpp4r::complexes result = cpp4r::as_complexes(x);
7+
8+
expect_true(result.size() == 3);
9+
expect_true(result[0] == cpp4r::r_complex(1, 0));
10+
expect_true(result[1] == cpp4r::r_complex(2, 0));
11+
expect_true(result[2] == cpp4r::r_complex(3, 0));
12+
}
13+
14+
test_that("as_complexes handles NA_INTEGER") {
15+
cpp4r::writable::integers x({NA_INTEGER, 42});
16+
cpp4r::complexes result = cpp4r::as_complexes(x);
17+
18+
expect_true(cpp4r::is_na(result[0]));
19+
expect_true(result[1] == cpp4r::r_complex(42, 0));
20+
}
21+
22+
test_that("as_complexes from complex passthrough") {
23+
cpp4r::writable::complexes x({cpp4r::r_complex(1, 2)});
24+
cpp4r::complexes result = cpp4r::as_complexes(x);
25+
expect_true(result[0] == cpp4r::r_complex(1, 2));
26+
}
27+
28+
test_that("as_complexes throws on invalid type") {
29+
cpp4r::writable::strings x({"a", "b"});
30+
expect_error(cpp4r::as_complexes(x));
31+
}
32+
33+
test_that("complex_vector wrapper works") {
34+
SEXP x = PROTECT(Rf_allocVector(CPLXSXP, 2));
35+
COMPLEX(x)[0].r = 1.0;
36+
COMPLEX(x)[0].i = 2.0;
37+
COMPLEX(x)[1].r = 3.0;
38+
COMPLEX(x)[1].i = 4.0;
39+
40+
cpp4r::complex_vector cv(x);
41+
expect_true(cv.size() == 2);
42+
expect_true(cv[0] == std::complex<double>(1.0, 2.0));
43+
expect_true(cv[1] == std::complex<double>(3.0, 4.0));
44+
UNPROTECT(1);
45+
}
46+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <testthat.h>
2+
3+
context("attribute_proxy-C++") {
4+
test_that("attribute_proxy with initializer_list") {
5+
cpp4r::writable::doubles x({1., 2., 3.});
6+
x.attr("custom") = {10, 20, 30};
7+
8+
cpp4r::integers attr_val(Rf_getAttrib(x.data(), Rf_install("custom")));
9+
expect_true(attr_val[0] == 10);
10+
expect_true(attr_val[1] == 20);
11+
expect_true(attr_val[2] == 30);
12+
}
13+
14+
test_that("attribute_proxy with SEXP name") {
15+
cpp4r::writable::integers x({1, 2, 3});
16+
SEXP sym = Rf_install("foo");
17+
x.attr(sym) = "bar";
18+
19+
cpp4r::strings val(Rf_getAttrib(x.data(), sym));
20+
expect_true(val[0] == "bar");
21+
}
22+
23+
test_that("attribute_proxy read via SEXP conversion") {
24+
cpp4r::writable::doubles x({1., 2.});
25+
x.attr("test") = 42;
26+
27+
SEXP attr_val = x.attr("test");
28+
expect_true(INTEGER(attr_val)[0] == 42);
29+
}
30+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <testthat.h>
2+
3+
context("environment-advanced-C++") {
4+
test_that("environment::exists() works") {
5+
auto new_env = cpp4r::package("base")["new.env"];
6+
cpp4r::environment x(new_env());
7+
8+
expect_false(x.exists("foo"));
9+
x["foo"] = 1;
10+
expect_true(x.exists("foo"));
11+
}
12+
13+
test_that("environment::exists() with SEXP symbol") {
14+
auto new_env = cpp4r::package("base")["new.env"];
15+
cpp4r::environment x(new_env());
16+
17+
SEXP sym = Rf_install("bar");
18+
expect_false(x.exists(sym));
19+
x["bar"] = 2;
20+
expect_true(x.exists(sym));
21+
}
22+
23+
test_that("environment::exists() with std::string") {
24+
auto new_env = cpp4r::package("base")["new.env"];
25+
cpp4r::environment x(new_env());
26+
27+
std::string name = "baz";
28+
expect_false(x.exists(name));
29+
x["baz"] = 3;
30+
expect_true(x.exists(name));
31+
}
32+
33+
test_that("environment::remove() with SEXP symbol") {
34+
auto new_env = cpp4r::package("base")["new.env"];
35+
cpp4r::environment x(new_env());
36+
37+
x["foo"] = 1;
38+
expect_true(x.exists("foo"));
39+
40+
SEXP sym = Rf_install("foo");
41+
x.remove(sym);
42+
expect_false(x.exists("foo"));
43+
}
44+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <testthat.h>
2+
3+
context("function-advanced-C++") {
4+
test_that("function with multiple named arguments") {
5+
using namespace cpp4r::literals;
6+
7+
auto paste = cpp4r::package("base")["paste"];
8+
std::string res = cpp4r::as_cpp<std::string>(paste("a", "b", "c", "sep"_nm = "-"));
9+
expect_true(res == "a-b-c");
10+
}
11+
12+
test_that("package::operator[] with std::string") {
13+
std::string func_name = "sum";
14+
auto sum = cpp4r::package("base")[func_name];
15+
double result = sum(cpp4r::as_sexp({1., 2., 3.}));
16+
expect_true(result == 6.);
17+
}
18+
19+
test_that("cpp4r::message works") {
20+
// Just test that it doesn't throw - message output is captured by R
21+
cpp4r::message("test message");
22+
expect_true(true);
23+
}
24+
25+
test_that("cpp4r::message with format args") {
26+
cpp4r::message("test %s %d", "message", 42);
27+
expect_true(true);
28+
}
29+
30+
test_that("cpp4r::message with std::string") {
31+
std::string msg = "test message";
32+
cpp4r::message(msg);
33+
expect_true(true);
34+
}
35+
}

0 commit comments

Comments
 (0)