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:
> 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:
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 helper functions wrap()
to return any option to a SEXP
, and as<T>()
which (as a templated function) converts from a SEXP
to the template type.
> cppFunction("CharacterVector cnvrt(NumericVector x) \ # indented for display
{ return as<CharacterVector>(x); }") # else really one line
> cnvrt(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 some time-honoured rules:
SEXP
and then from SEXP
to your destination object