Chapter 18 RObject
The RObject
type is a type that can be assigned to any type of object defined in Rcpp. If you do not know what type is passed to the variable at run time, you can use RObject
.
18.1 Member functions
RObject
has the following member functions. These member functions also exist in all other API classes (such as NumericVector
) in Rcpp.
18.1.1 inherits(str)
Returns true
if this object inherits the class specified by the string str
.
18.1.2 slot(name)
Accesses the slot specified by the character string name
if this object is S4
.
18.1.3 hasSlot(name)
Returns true
if there is a slot specified by the character string name.
18.1.4 attr(name)
Accesses the attribute specified by the string name
.
18.1.5 attributeNames()
Return the names of all the attributes of this object as std::vector<std::string>
.
18.1.6 hasAttribute(name)
Returns true
if this object has an attribute with the name specified by the string name
.
18.1.7 isNULL()
Returns true if this object is NULL
.
18.1.8 sexp_type()
Returns SXPTYPE
of this object as int
. See the R internals for a list of all SEXPTYPE
defined in R.
18.1.9 isObject()
Returns true
if this object has a “class” attribute.
18.1.10 isS4()
Return true
if this object is a S4
object.
18.2 Determining type of object assigned to RObject
One useful use of RObject
is to determine the type of the object. To determine which type the value assigned to RObject
, use the is<T>()
function or member function isS4()
isNULL()
.
However, matrices and factor vectors can not be determined by only using the function is<T>()
because they are vectors with values are set to specific attribute. To determine them, use Rf_isMatrix()
function or the Rf_isFactor()
function.
The code example below shows how to determine the type using RObject
.
// [[Rcpp::export]]
void rcpp_type(RObject x){
if(is<NumericVector>(x)){
if(Rf_isMatrix(x)) Rcout << "NumericMatrix\n";
else Rcout << "NumericVector\n";
}
else if(is<IntegerVector>(x)){
if(Rf_isFactor(x)) Rcout << "factor\n";
else Rcout << "IntegerVector\n";
}
else if(is<CharacterVector>(x))
Rcout << "CharacterVector\n";
else if(is<LogicalVector>(x))
Rcout << "LogicalVector\n";
else if(is<DataFrame>(x))
Rcout << "DataFrame\n";
else if(is<List>(x))
Rcout << "List\n";
else if(x.isS4())
Rcout << "S4\n";
else if(x.isNULL())
Rcout << "NULL\n";
else
Rcout << "unknown\n";
}
Use as<T>()
to convert RObject
to another Rcpp type after determining the type.
18.3 Using RObject
to write functions that receive various datatypes (function templates)
C++ allows us to avoid code duplication by writing templated functions that are not limited to specific datatypes.
Note: This section does not cover C++ templates in any detail - it only shows how to implement them in Rcpp. It also assumes your compiler can use C++11 syntax. If not, please refer to this Rcpp Gallery example by Nathan Russell which covers the process in much more detail (and which provided the source material for this section).
Say we have a very simple C++ templated function that prints “Hello” and then returns its input.
The T
in this function is a placeholder for any other type. You can use any identifier you like, but T
is conventional. At compile time the compiler analyses our code and creates new functions with T
replaced with the relevant datatypes, so we don’t have to write versions of add_one()
for int
, float
, double
etc.
In Rcpp the process is almost identical, but we have to jump through a couple of extra hoops.
A first attempt at an Rcpp version of the function might look very similar:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
template<typename T>
T say_hello(T x) {
Rcout << "Hello!" << std::endl;
return x;
}
However, this will not compile. To quote Nathan Russell’s article:
Sadly this does not work: magical as Rcpp attributes may be, there are limits to what they can do, and at least for the time being, translating C++ template functions into something compatible with R’s C API is out of the question.
Fortunately Rcpp provides two macros, RCPP_RETURN_VECTOR
and RCPP_RETURN_MATRIX
that make it very simple to implement a templated function.
template<int RTYPE>
Vector<RTYPE> printer(Vector<RTYPE> x) {
Rcout << "Hello!" << std::endl;
return x;
}
// [[Rcpp::export]]
RObject say_hello(RObject x) {
RCPP_RETURN_VECTOR(printer, x);
}
The exported say_hello()
function now works as expected in R.
say_hello("a")
## Hello!
## [1] "a"
say_hello(1:10)
## Hello!
## [1] 1 2 3 4 5 6 7 8 9 10
say_hello(FALSE)
## Hello!
## [1] FALSE
Note that the template specification looks slightly different: template<int RTYPE>
rather than template<typename T>
in the earlier example. This is called a non-type parameter, and is used because the type of an RObject is stored as an integer. Again, Nathan’s article provides more details on this. As with the use of T
in the first example, the use of RTYPE
as an alias for the type is entirely arbitrary.
Also note that printer()
and say_hello()
have different names because otherwise the call to RCPP_RETURN_VECTOR
is ambiguous and the code will not compile. If you would prefer both functions to have the same name, you can use namespaces as shown below.