-1

I am developing a package for R and would like to load the functionalities of an R package inside an Rcpp function.

I know about Rcpp::Environment env("package:package_of_interest"), but this only works when the "package_of_interest" is loaded from R via library("package_of_interest"). Furthermore, I added the "package_of_interest" in the DESCRIPTION file below DEPENDS. That means, whenever I load my package with library("my_package") in R all functionalities are given as the desired package is auto-loaded. However, if I call the function via my_package::my_functionX(...) it drops an error and states that it can't convert the object to an environment: [type=character; target=ENVSXP]. enter image description here Is there any way to solve this problem? Can I convert the input (character) to target (ENVSXP)?

I previously studied Call a function from c++ via environment Rcpp and Rcpp trouble importing 'hessian' from R package 'numDeriv' and was confirmed with my approach.

In the following, I give you a minimal example to reproduce the error. With library(bigmemory) called first, this will work, but without not.

#include <iostream>
// [[Rcpp::depends(RcppArmadillo, BH, bigmemory)]]
#include <bigmemory/BigMatrix.h>
#include <bigmemory/MatrixAccessor.hpp>
#include <RcppArmadillo.h>
#include <Rdefines.h>
    
    
// [[Rcpp::export]]
void test_make_bm(std::size_t nrows, std::size_t ncols, std::string type, 
    bool separated = false, bool binarydescriptor = false, bool shared = true)
{

    // this line drops the error
    Rcpp::Environment bigmemory = Rcpp::Environment("package:bigmemory");
    // you can also directly call Rcpp::Environment bigmemory("package:bigmemory")
    
    Rcpp::Function big_matrix = bigmemory["big.matrix"];
            
    SEXP bm = big_matrix(
            Rcpp::_["nrow"] = nrows,
            Rcpp::_["ncol"] = ncols,
            Rcpp::_["type"] = type,
            Rcpp::_["init"] = R_NilValue,
            Rcpp::_["dimnames"] = R_NilValue,
            Rcpp::_["separated"] = separated,
            Rcpp::_["backingfile"] = R_NilValue,
            Rcpp::_["backingpath"] = R_NilValue,
            Rcpp::_["descriptorfile"] = R_NilValue,
            Rcpp::_["binarydescriptor"] = binarydescriptor,
            Rcpp::_["shared"] = shared);
        
    SEXP address = GET_SLOT(bm, Rf_install("address"));
    Rcpp::XPtr<BigMatrix> xptr(address);
        
    arma::mat m((double*) xptr->matrix(), xptr->nrow(), xptr->ncol());
    m.print();
    Rcpp::Rcout << "\n";
          
    return;

}

Is there any way to load the package from Rcpp? Does anyone know why bigmemory's BigMatrix has no constructor in C++?

G4lActus
  • 64
  • 7
  • You write "As long as you load library(bigmemory) first, this will work, but without not." and when you step back for second and reflect you realize that a package must be attached (which is what calling `library()` does for you) for you to access its environment. – Dirk Eddelbuettel Jan 15 '23 at 16:37

2 Answers2

1

This is essentially the same as one of the unit test files, this has been documented behavior for as long as it existed. If you need a function from a package that is not yet attached, you need to go via the namespace.

> Rcpp::cppFunction(paste0("double mysd(NumericVector x) { ", 
       r"(Environment ns = Environment::namespace_env("stats");)", 
       r"(Function f = ns["sd"]; return as<double>(f(x)); })"))
> mysd(1:10)
[1] 3.02765
> mysd(1:10)
[1] 3.02765
> 

(I split the cppFunction() call over three lines for readability, but you can make it just one which is also how I wrote it to test this.)

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
0

So after some research, I present you the solution. In order to get access to the packagename::package_function() functionality you have to use the namespace_env function from the Environment namespace.

#include <iostream>
// [[Rcpp::depends(RcppArmadillo, BH, bigmemory)]]
#include <bigmemory/BigMatrix.h>
#include <bigmemory/MatrixAccessor.hpp>
#include <RcppArmadillo.h>
#include <Rdefines.h>

// [[Rcpp::export]]
void test_make_bm(std::size_t nrows, std::size_t ncols, std::string type, 
    bool separated = false, bool binarydescriptor = false, bool shared = true)
{
Rcpp::Environment bigmemory = Rcpp::Environment::namespace_env("bigmemory");
   Rcpp::Function big_matrix = bigmemory["big.matrix"];
            
   SEXP bm = big_matrix(
            Rcpp::_["nrow"] = nrows,
            Rcpp::_["ncol"] = ncols,
            Rcpp::_["type"] = type,
            Rcpp::_["init"] = R_NilValue,
            Rcpp::_["dimnames"] = R_NilValue,
            Rcpp::_["separated"] = separated,
            Rcpp::_["backingfile"] = R_NilValue,
            Rcpp::_["backingpath"] = R_NilValue,
            Rcpp::_["descriptorfile"] = R_NilValue,
            Rcpp::_["binarydescriptor"] = binarydescriptor,
            Rcpp::_["shared"] = shared);
        
    SEXP address = GET_SLOT(bm, Rf_install("address"));
    Rcpp::XPtr<BigMatrix> xptr(address);
        
    arma::mat m((double*) xptr->matrix(), xptr->nrow(), xptr->ncol());
    m.print();
    Rcpp::Rcout << "\n";
          
    return;   
}
Henry Ecker
  • 34,399
  • 18
  • 41
  • 57
G4lActus
  • 64
  • 7
  • This is documented behaviour in the package (so not really novel), longer than it needs to be, and completely independent of package `bigmemory` or any other package. – Dirk Eddelbuettel Jan 15 '23 at 17:40
  • I absolutely agree with you that it is absolutely independent of the package `big memory` and I never claimed that is solely related to it. I was also aware of the attached libraries etc. The only thing that I was not aware of from the start, was that the Environment namespace has the function namespace_env. If I use this function rather than just the Environment constructor, I become independent from calling the library first and can ensure that the Rcpp function works properly. Otherwise, the above-mentioned error can drop. Thanks for your contribution. – G4lActus Jan 15 '23 at 17:59
  • And regarding the bigmemory example, well it was just an example, as I am working with it currently. My last sentence in my original questions was more like a general question if anyone else might know how to create a bigmemory object at the C++ level without using R functionalities. Within the C++ functionalities of this package there seem to be no regular constructor to create a class instance. – G4lActus Jan 15 '23 at 18:34
  • 1
    *Minimal* examples and answers are so much easier to comprehend and grok. See [this standard page of StackOverflow documentation](https://stackoverflow.com/help/minimal-reproducible-example) for more -- while focused on _examples_ the same goes for _answers_. – Dirk Eddelbuettel Jan 15 '23 at 19:04