2

Say I have an R package called "packA" that contains the following file "funcA.cpp":

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::interfaces(r, cpp)]]
#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcA (arma::vec& x) {
  x += 1;
}

Calling this function from R yields the expected result of adding 1 to each element of the vector in-place:

> vec <- c(1, 2, 3)
> funcA(vec)
> vec
[1] 2 3 4

Now say I have a second package "packB" that wants to call the function "funcA". It contains the following file called "funcB.cpp":

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <packA.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcB() {
  arma::vec x = {1, 2, 3};

  Rcout << x << "\n";
  packA::funcA(x);
  Rcout << x << "\n";
}

Calling this function no longer yields the desired result, as it would appear that the vector x is no longer modified in-place:

> funcB()
   1.0000
   2.0000
   3.0000

   1.0000
   2.0000
   3.0000

Is there any way to preserve the in-place operations of C++ functions while still using Rcpp? Thanks in advance for any advice.

Edit: I modified "funcB.cpp" according to Dirk Eddelbüttel's suggestion:

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <packA.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcB(arma::vec& y) {
  packA::funcA(y);
}

Unfortunately, the result when calling the function from R was the same:

> vec <- c(1, 2, 3)
> funcB(vec)
> vec
[1] 1 2 3 

Edit 2: After some further experimentation I noticed that things start to break down as soon as arma::vec& x is in the footprint of funcA. Simply using a NumericVector works:

"funcA.cpp"

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::interfaces(r, cpp)]]
#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcA (NumericVector& num_x) {
  num_x[2] = 10 + num_x[2];
}
> vec <- c(1, 2, 3)
> funcA(vec)
> vec
[1]  1  2 13

"funcB.cpp"

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <packA.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcB(NumericVector& num_y) {
  packA::funcA(num_y);
}

> vec <- c(1, 2, 3)
> funcB(vec)
> vec
[1]  1  2 13

Even using the advanced arma::vec constructor to create an Armadillo vector with shared memory works as long as the input is a NumericVector:

"funcA.cpp"

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::interfaces(r, cpp)]]
#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcA (NumericVector& num_x) {
  arma::vec x_int = arma::vec(num_x.begin(), 3, false, false);
  x_int(2) = 10 + x_int(2);
}
> vec <- c(1, 2, 3)
> funcA(vec)
> vec
[1]  1  2 13

> vec <- c(1, 2, 3)
> funcB(vec)
> vec
[1]  1  2 13

Trying the same thing with an arma::vec no longer works.

"funcA.cpp"

// [[Rcpp::depends(RcppArmadillo)]]
// [[Rcpp::interfaces(r, cpp)]]
#include <RcppArmadillo.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcA (arma::vec& x) {
  arma::vec x_int = arma::vec(x.begin(), 3, false, false);
  x(2) = 10 + x(2);
}
> vec <- c(1, 2, 3)
> funcA(vec)
> vec
[1]  1  2 13

"funcB.cpp"

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <packA.h>
using namespace Rcpp;

// [[Rcpp::export]]
void funcB(arma::vec& y) {
  packA::funcA(y);
}
> vec <- c(1, 2, 3)
> funcB(vec)
> vec
[1] 1 2 3

1 Answers1

1

I think that is a different topic. When you call A, you create an R vector. That R vector is constructed with R memory, which R "own" and which we let Armadillo reuse. In-place is possible there.

But when you run B, you construct an Armadillo vector not owned by R. That is not "our" memory in R, and the content only ever comes back via a copy. And you loose the in-place mod. Make that function have an arma::vec signature, and pass an existing vector instead.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • First off, thanks for taking the time to respond! I edited the question according to your suggestion. Unfortunately, the result when calling the function from R was the same. Additionally, my use case would actually require vectors created in C++ to be edited in place, as in the original example. Do you think this would still be possible, while still working with Rcpp? – NeferkareII Aug 17 '20 at 11:50
  • 1
    There is no _fundamental reason_ why it shouldn't. All Rcpp does is help you with the underlying `SEXP .Call("somefun", SEXP a, SEXP b, ...)` calls. And the `SEXP` are still _pointers_. Yet it is possibly for you to 'break the the chain' along the way. – Dirk Eddelbuettel Aug 17 '20 at 11:55
  • Would you have any pointers (pun slightly intended) as to where such a break in the chain is most likely to occur? – NeferkareII Aug 17 '20 at 13:22
  • 1
    Start with the simplest case and change your interfaces to `SEXP sx` and instantiate to / convert back from an `arma::vec` as one does. Maybe try the same with `Rcpp::NumericVector`. At some point you will see a difference. There may even be a bug somewhere, though these interfaces are fairly trusted and stable and have not changed in some time. – Dirk Eddelbuettel Aug 17 '20 at 14:08
  • I played around with it a bit more and noticed that things seem to stop working as soon as `arma::vec&` is used. Both `SEXP` and `NumericVector` worked as expected (see my edit for the `NumericVector` case). Is there something I am still missing/not understanding? – NeferkareII Aug 19 '20 at 11:01
  • 1
    Well the function signatures are all there for you to study, and if you think you found a reproducible "bug" feel free to report it. Maybe this is due to the `const` and `&` correctness changes for `arma::vec` from/to R. I note that what you are chasing here is _not_ a promised feature. We only say things about R interfaces to/from Armadillo. – Dirk Eddelbuettel Aug 19 '20 at 12:12