NeferkareII
NeferkareII

Reputation: 21

How to make a C++ function that performs an in-place operation available to another R package using Rcpp?

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

Upvotes: 2

Views: 193

Answers (1)

Dirk is no longer here
Dirk is no longer here

Reputation: 368181

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.

Upvotes: 1

Related Questions