Loading a R package's function from Rcpp function in a new package namespace

228 Views Asked by At

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++?

2

There are 2 best solutions below

4
G4lActus On BEST ANSWER

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;   
}
0
Dirk is no longer here On

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.)