c++rpointersrcpp

How to use XPtr


I am having trouble wrapping my head around how to use Rcpp::XPtr.

I want to pass an R list of integer vectors to C++, convert it to std::vector<std::unordered_set<int> >, return it to R as an Rcpp::XPtr, and reuse it in a later C++ function.

// [[Rcpp::plugins(openmp)]]
#include <Rcpp.h>
#include <vector>
#include <unordered_set>
#include <omp.h>
#include <cstddef>

// [[Rcpp::export]]
Rcpp::XPtr<std::vector<std::unordered_set<int> > > convert_to_cpp_type(Rcpp::List& x) {
  std::size_t x_size = x.size();
  std::vector<std::unordered_set<int> > y(x_size);

  for(std::size_t i = 0; i < x_size; ++i) {
    Rcpp::IntegerVector x_i = x[i];
    y[i].insert(x_i.begin(), x_i.end());
  }

  Rcpp::XPtr<std::vector<std::unordered_set<int> > > z(y);
  return z;
}

// [[Rcpp::export]]
Rcpp::NumericVector use_xptr(SEXP a) {
  Rcpp::XPtr<std::vector<std::unordered_set<int> > > b(a);
  std::size_t b_size = (*b).size();
  std::vector<double> c (b_size);  

  #pragma omp parallel for num_threads(10)
  for(std::size_t i = 0; i < b_size; ++i) {
    c[i] = example_function((*b)[i]);
  }

  return Rcpp::wrap(c);
}

This code does not compile, but it should provide an idea of what I aim to do. a in the second function is the XPtr exported by the first function.

There should be multiple errors in this code. However, even after hours of browsing Stack Overflow, the Rcpp website, and various other sites, I did not figure out how to correctly implement this.


Solution

  • Your first function doesn't compile because the XPtr constructor needs a raw pointer, not a std::vector. However, the returned pointer will be worse than useless because it is pointing to a local variable that goes out of scope after the function returns. If you try to use it later then your session will crash.

    The following function will return a valid pointer to a std::vector<std::unordered_set>>:

    library(Rcpp)
    
    cppFunction("
    
    Rcpp::XPtr<std::vector<std::unordered_set<int> > > 
    convert_to_cpp_type(Rcpp::List x) {
    
      typedef std::vector<std::unordered_set<int>> obj;
      std::size_t x_size = x.size();
      obj* y = new obj;
    
      for(std::size_t i = 0; i < x_size; ++i) {
        Rcpp::IntegerVector x_i = x[i];
        std::unordered_set s(x_i.begin(), x_i.end());
        y->push_back(s);
      }
    
      Rcpp::XPtr<obj> z(y);
      return z;
      
    }")
    

    To get the contents of the object indexed by the pointer, we need to build an R list again:

    cppFunction("
    
    Rcpp::List use_xptr(SEXP a) {
    
      Rcpp::XPtr<std::vector<std::unordered_set<int>>> b(a);
      Rcpp::List out;
      for(std::size_t i = 0; i < b->size(); ++i) {
        Rcpp::NumericVector x_i((*b)[i].begin(), (*b)[i].end());
        out.push_back(x_i);
      }
      return out;
      
    }")
    

    For completeness, let's create a function that we can call from R which modifies our C++ object (since we don't have example_function

    cppFunction("
    
    void do_stuff(Rcpp::XPtr<std::vector<std::unordered_set<int>>> x) {
    
      x->push_back(std::unordered_set<int> {0, 1, 2, 3});
      return;
    
    }")
    

    Now in R we can do:

    x <- list(1:10, 3:7)
    
    x
    #> [[1]]
    #>  [1]  1  2  3  4  5  6  7  8  9 10
    #> 
    #> [[2]]
    #> [1] 3 4 5 6 7
    
    xptr <- convert_to_cpp_type(x)
    
    xptr
    #> <pointer: 0x0000023659ab57c0>
    
    do_stuff(xptr)
    
    use_xptr(xptr)
    #> [[1]]
    #> [1] 10  9  8  7  6  5  4  3  2  1
    #> 
    #> [[2]]
    #> [1] 7 6 5 4 3
    #> 
    #> [[3]]
    #> [1] 3 2 1 0
    

    This seems like an unnecessarily difficult way to do things. It would seem to me to make more sense to send a list to C++, have it do all the calculations you want, and return the result. The pointer doesn't really help you here.