I'm frequently using std::map<std::string, arma::vec>
in c++ so I wrote custom as
and wrap
templates to handle the R-C++ conversion. Below is a minimal reprex:
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
// forward declarations
namespace Rcpp
{
template <>
inline std::map<std::string, arma::vec> as(SEXP matsexp)
{
Rcpp::NumericMatrix mat(matsexp);
std::vector<std::string> cn = Rcpp::as<std::vector<std::string>>(Rcpp::colnames(mat));
std::map<std::string, arma::vec> map;
for (unsigned int n = 0; n < mat.ncol(); n++)
{
map[cn[n]] = mat.column(n);
}
return map;
}
template <>
inline SEXP wrap(const std::map<std::string, arma::vec> &map)
{
Rcpp::NumericMatrix mat(map.begin()->second.n_elem, map.size());
// Get all keys of the map
std::vector<std::string> keys;
for (auto const &x : map)
{
keys.push_back(x.first);
}
// Get all values of the map
std::vector<arma::vec> values;
for (auto const &x : map)
{
values.push_back(x.second);
}
// Fill the matrix
for (unsigned int n = 0; n < mat.ncol(); n++)
{
for (unsigned int m = 0; m < mat.nrow(); m++)
{
mat(m, n) = values[n](m);
}
}
// Set column names
Rcpp::CharacterVector colnames(keys.size());
for (unsigned int n = 0; n < keys.size(); n++)
{
colnames[n] = keys[n];
}
Rcpp::colnames(mat) = colnames;
return Rcpp::wrap(mat);
}
}
// [[Rcpp::export]]
std::map<std::string, arma::vec> in_and_out(std::map<std::string, arma::vec> &map)
{
return map;
}
class Foo
{
public:
Foo() = default;
std::map<std::string, arma::vec> testmap;
};
RCPP_MODULE(FooEx)
{
using namespace Rcpp;
class_<Foo>("Foo")
.constructor()
.field("testmap", &Foo::testmap);
}
/***R
A <- matrix(1:9, ncol = 3, dimnames = list(NULL, c("a", "b", "c")))
print("Using the Foo class from within R")
foo <- new(Foo)
foo$testmap <- A
print(foo$testmap)
print("Calling in_and_out()")
in_and_out(A)
*/
The above works like a charm so I tried to bring that stuff into a package. I made a minimal package to demonstrate the problem. It can be found here. This is what I did:
- Created package skeleton using
Rcpp::Rcpp.package.skeleton(module = TRUE)
- Removed stuff
- Added /inst/include/anRpackage.h which contains the
as
andwrap
templates. This file will be added toRcppExports.cpp
automatically (see 2.5) - Added /src/foo.cpp which contains the class definition and module export
- Added in_and_out.cpp contains the
in_and_out
function (as above) just for demo purposes - Called
Rcpp::compileAttributes()
anddevtools::load_all()
Compilation fails with:
192 | map(const _Compare& __comp,
| ~~~~~~~~~~~~~~~~^~~~~~
/usr/include/c++/9/bits/stl_map.h:183:7: note: candidate: ‘std::map<_Key, _Tp, _Compare, _Alloc>::map() [with _Key = std::__cxx11::basic_string<char>; _Tp = arma::Col<double>; _Compare = std::less<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, arma::Col<double> > >]’
183 | map() = default;
| ^~~
/usr/include/c++/9/bits/stl_map.h:183:7: note: candidate expects 0 arguments, 1 provided
make: *** [/usr/lib/R/etc/Makeconf:177: foo.o] Error 1
ERROR: compilation failed for package ‘anRpackage’
Commenting out .field("testmap", &Foo::testmap)
makes compilation successful and in_and_out can be used without problems.
Any Idea why it is working using sourceCpp
but the same code does not compile using the package.skeleton?
Thanks in advance.