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.
CodePudding user response:
I can see the error you post (on map()
) but I think that may be a red herring. When I try to build and install your package (and after I change your for
loop index variable from unsigned
to standard signed int
to avoid a little bit of noise) I see compilation failing with
edd@rob:/tmp/berri/rcpp_test(master)$ install2.r -l ../lib/ anRpackage_1.0.tar.gz
* installing *source* package ‘anRpackage’ ...
** using staged installation
** libs
ccache g -11 -I"/usr/share/R/include" -DNDEBUG -I'/usr/local/lib/R/site-library/Rcpp/include' -I'/usr/local/lib/R/site-library/RcppArmadillo/include' -fpic -g -O3 -Wall -pipe -pedantic -c RcppExports.cpp
-o RcppExports.o
ccache g -11 -I"/usr/share/R/include" -DNDEBUG -I'/usr/local/lib/R/site-library/Rcpp/include' -I'/usr/local/lib/R/site-library/RcppArmadillo/include' -fpic -g -O3 -Wall -pipe -pedantic -c foo.cpp -o foo.o
In file included from /usr/local/lib/R/site-library/Rcpp/include/Rcpp/as.h:25,
from /usr/local/lib/R/site-library/Rcpp/include/RcppCommon.h:168,
from /usr/local/lib/R/site-library/RcppArmadillo/include/RcppArmadilloForward.h:25,
from /usr/local/lib/R/site-library/RcppArmadillo/include/RcppArmadillo.h:29,
from foo.cpp:1:
/usr/local/lib/R/site-library/Rcpp/include/Rcpp/internal/Exporter.h: In instantiation of ‘Rcpp::traits::Exporter<T>::Exporter(SEXP) [with T = std::map<std::__cxx11::basic_string<char>, arma::Col<double> >; SEXP
= SEXPREC*]’:
/usr/local/lib/R/site-library/Rcpp/include/Rcpp/as.h:87:41: required from ‘T Rcpp::internal::as(SEXP, Rcpp::traits::r_type_generic_tag) [with T = std::map<std::__cxx11::basic_string<char>, arma::Col<double> >;
SEXP = SEXPREC*]’
/usr/local/lib/R/site-library/Rcpp/include/Rcpp/as.h:152:31: required from ‘T Rcpp::as(SEXP) [with T = std::map<std::__cxx11::basic_string<char>, arma::Col<double> >; SEXP = SEXPREC*]’
/usr/local/lib/R/site-library/Rcpp/include/Rcpp/module/Module_Field.h:36:72: required from ‘void Rcpp::class_<Class>::CppProperty_Getter_Setter<PROP>::set(Class*, SEXP) [with PROP = std::map<std::__cxx11::basi
c_string<char>, arma::Col<double> >; Class = Foo; SEXP = SEXPREC*]’
/usr/local/lib/R/site-library/Rcpp/include/Rcpp/module/Module_Field.h:36:10: required from here
/usr/local/lib/R/site-library/Rcpp/include/Rcpp/internal/Exporter.h:31:42: error: no matching function for call to ‘std::map<std::__cxx11::basic_string<char>, arma::Col<double> >::map(SEXPREC*&)’
31 | Exporter( SEXP x ) : t(x){}
| ^~~~
and that too me suggests that you simply may not have gotten as far as you had hoped. You are struggling with the (arguably non-trivial) part of writing extenders. So if I were you I would maybe work on deciding first whether I would want as<>
or wrap()
convenience, or whether I want Modules -- as I can't immediately think of an example package that does both. Some of the setup between the two may (or may not, I am not perfectly sure on that) get in the way of the other.
Personally, I am also happier with the direct wrappers around R CMD build
and R CMD check
(where I do use the rcmdcheck
package) as I find that devtools
obfuscates things and makes it harder to see what is, or isn't happening. But those are just personal preferences; you seem to be a big fan of devtools
so maybe you will end up making it work for yourself (c.f. your unanswered question on the mailing list).
I like Modules for their simplicity. I also like simple-enough uses of the as<>()
and wrap()
extenders. Your suggested use in in_and_out.cpp
is ambitious. Maybe just using an external pointer so such structures or maps is quicker -- it is hard to tell.
Edit: As discussed, I was curious aboud a quick minimal existence proof. I just sent you a reduced package doing the as<>()
and wrap()
converters in this PR #1 to your repo.