Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Coerce a vector to another type in Rcpp

In R, you can coerce between vector types using as.X, e.g. as.character(1) or as.integer(1). However I can’t work out if there is a native, elegant way to do the same in Rcpp.

Firstly, the C API does have an AS_CHARACTER macro which I tried to use, but Rcpp doesn’t seem to include Rdefines.h where it’s defined:

> as_character = Rcpp::cppFunction("CharacterVector as_character(RObject x){ return AS_CHARACTER(x); }")
file113af2570bd06.cpp: In function ‘Rcpp::CharacterVector as_character(Rcpp::RObject)’:
file113af2570bd06.cpp:6:49: error: ‘AS_CHARACTER’ was not declared in this scope
    6 | CharacterVector as_character(RObject x){ return AS_CHARACTER(x); }
      |                                                 ^~~~~~~~~~~~
make: *** [/usr/lib/R/etc/Makeconf:178: file113af2570bd06.o] Error 1
g++ -std=gnu++14 -I"/usr/share/R/include" -DNDEBUG   -I"/home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include" -I"/tmp/RtmppCAKV8/sourceCpp-x86_64-pc-linux-gnu-1.0.10"    -fpic  -g -O2 -ffile-prefix-map=/build/r-base-LhKvHL/r-base-4.2.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2  -c file113af2570bd06.cpp -o file113af2570bd06.o
Error in sourceCpp(code = code, env = env, rebuild = rebuild, cacheDir = cacheDir,  : 
  Error 1 occurred building shared library.

If I try to use CharacterVector, it also fails to compile:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

> as_character = Rcpp::cppFunction(
+ "CharacterVector as_character(RObject x){ return CharacterVector::create(x); }"
+ )
In file included from /home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include/Rcpp/Vector.h:50,
                 from /home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include/Rcpp.h:40,
                 from file113af65cda53c.cpp:1:
/home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include/Rcpp/vector/converter.h: In instantiation of ‘static SEXPREC* Rcpp::internal::string_element_converter<RTYPE>::get(const T&) [with T = Rcpp::RObject_Impl<Rcpp::PreserveStorage>; int RTYPE = 16; SEXP = SEXPREC*]’:
/home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include/Rcpp/generated/Vector__create.h:84:34:   required from ‘static Rcpp::Vector<RTYPE, StoragePolicy> Rcpp::Vector<RTYPE, StoragePolicy>::create__dispatch(Rcpp::traits::false_type, const T1&) [with T1 = Rcpp::RObject_Impl<Rcpp::PreserveStorage>; int RTYPE = 16; StoragePolicy = Rcpp::PreserveStorage; Rcpp::traits::false_type = Rcpp::traits::integral_constant<bool, false>]’
/home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include/Rcpp/generated/Vector__create.h:71:26:   required from ‘static Rcpp::Vector<RTYPE, StoragePolicy> Rcpp::Vector<RTYPE, StoragePolicy>::create(const T1&) [with T1 = Rcpp::RObject_Impl<Rcpp::PreserveStorage>; int RTYPE = 16; StoragePolicy = Rcpp::PreserveStorage]’
file113af65cda53c.cpp:6:72:   required from here
/home/migwell/R/x86_64-pc-linux-gnu-library/4.2/Rcpp/include/Rcpp/vector/converter.h:49:37: error: no matching function for call to ‘std::__cxx11::basic_string<char>::basic_string(const Rcpp::RObject_Impl<Rcpp::PreserveStorage>&)’
   49 |                         std::string out(input) ;
      |   

On the other hand, it does allow Rf_coerceVector which does work, but it seem unlikely this is the best way, because I’m fairly deep into pure C if I’m using STRSXP and Rf_ functions:

> as_character = Rcpp::cppFunction(
+ "CharacterVector as_character(RObject x){ return Rf_coerceVector(x, STRSXP); }"
+ )
> as_character(1)
[1] "1"

What is the most natural/simple way to do this in Rcpp?

I’m using:

  • R 4.2.3
  • Rcpp 1.0.10

>Solution :

If you take a second and closer look at the existing Rcpp documentation as provided by the ten vignettes, including two introductory ones and one focussed entirely on the conversions you ask about here, then you may encouter the two central idioms as wrap() to return any option to a SEXP, and as<T>() which (as a templated function) converts from a SEXP to the template type.

> Rcpp::cppFunction("CharacterVector converter(NumericVector x)  # indent display
                   { return Rcpp::as<CharacterVector>(x); }")    # else one line
> converter(1.23)
[1] "1.23"
> 

The thing that happens and is confusing is that the compiler most often automagically injects an existing conversion from or to SEXP, but it sometimes errors, or conflicts with other templated meta programming code (which is complicated, sadly). For example overloading the return statement often bites us.

So I have two time-honoured rules:

  • if in doubt, separate the out the assignment (and trust the compiler to re-optimise)
  • for conversions, maybe first explicitly go to SEXP and then from
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading