04 - Read-only versus Writable R Objects

1 Read and write

In cpp4r, when you declare a variable, it is read-only by default as in cpp11 Vaughan 2024. This is different from Rcpp, where variables are read-write by default EddelbuettelFrancois 2011.

Following from the package created in the previous vignette, try to compile and run the following code by adding it to a new file src/04_square_coordinates.h:

[[cpp4r::register]]
integers square_coordinates_(integers x) {
  integers out = x;
  for (int i = 0; i < x.size(); ++i) {
    out[i] = x[i] * x[i];
  }
  return out;
}

devtools::load_all() and devtools::install() will fail with the following error:

error: lvalue required as left operand of assignment
  out[i] = x[i] * x[i];

This is because the argument x is a read-only object, you need to explicitly declare a writable variable:

[[cpp4r::register]]
integers square_coordinates(integers x) {
  writable::integers out = x;
  for (int i = 0; i < x.size(); ++i) {
    out[i] = x[i] * x[i];
  }
  return out;
}

Alternatively, you can declare the argument as writable:

[[cpp4r::register]]
integers square_coordinates_2(writable::integers x) {
  for (int i = 0; i < x.size(); ++i) {
    x[i] = x[i] * x[i];
  }
  return x;
}

Do not forget to include the new header file in main.cpp:

Document these functions with roxygen and compare their outputs after loading the updated package:

cpp4r::register()
devtools::document()
devtools::load_all()

Compare square_coordinates(1:3), square_coordinates(c(1, 2, 3)), and square_coordinates(c(1L, 2L, 3L)). Why do the outputs differ? Are integers and doubles interchangeable as double and int in C++ for reasonable cases?

2 References

Eddelbuettel D, Francois R (2011). “Rcpp: Seamless R and C++ Integration.” Journal of Statistical Software, 40, 1–18. ISSN 1548-7660. doi:10.18637/jss.v040.i08. https://doi.org/10.18637/jss.v040.i08.

Vaughan D, Hester J, Francois R (2024). “Get started with cpp11.” https://cpp11.r-lib.org/articles/cpp11.html#intro.