[Rcpp-devel] Extract a function from a list and create a call
On Sat, Mar 20, 2010 at 2:53 AM, Romain Francois
<romain at r-enthusiasts.com> wrote:
Hello,
The issue with CharacterVector is that the proxy classes :
"string_name_proxy" and "string_proxy" only have implicit conversion to SEXP
and char* :
operator char* (){
? ? ? ?return get() ;
}
operator SEXP(){
? ? ? ?return ::Rf_mkString(get()) ;
}
i.e. they don't have implicit conversion to std::string. We can't have both
implicit conversions to char* and std::string because it causes ambiguities
and the compiler is much unhappy.
So given a CharacterVector, we can grab the first element as a std::string
using one of these options:
require( Rcpp )
require( inline )
fx <- cfunction( signature( x_ = "character" ), '
CharacterVector x(x_);
// x[0] is implicitely converted to a char* and the string( char*)
// constructor is used
std::string y( x[0] ) ;
// using string::operator=( char* ) with a std::string created before
std::string z ;
z = x[0] ;
// but this does not work
// std::string foo = x[0] ;
// because there is no implicit conversion proxy -> std::string
', Rcpp = TRUE, includes = "using namespace Rcpp;" )
I can run your example without a problem but when I try to go to the next stage and extract the character vectors from a list, it seems that internally the conversion to a STRSXP has not been completed (or something like that). Attempts to extract the first string from the CharacterVector extracted by name from the list fail with an error message of
fx(binomial())
Assigned fam of length 1, llink of length 1
Error in fx(binomial()) :
STRING_ELT() can only be applied to a 'character vector', not a 'symbol'
The test script is
suppressWarnings(require(inline))
suppressWarnings(require(Rcpp))
fx <- cfunction( signature( l = "list" ), '
List ll(l);
CharacterVector fam = ll["family"], llink = ll["link"];
Rprintf("Assigned fam of length %d, llink of length %d\\n",
fam.size(), llink.size());
char *fpt = (char *)0, *lpt = (char *)0;
if (fam.size()) fpt = fam[0];
if (llink.size()) lpt = llink[0];
return List::create(_["fam"] = std::string(fpt),
_["llink"] = std::string(lpt));
', Rcpp = TRUE, includes = "using namespace Rcpp;" )
fx(binomial())
fx(gaussian())
fx(poisson())
Le 19/03/10 21:42, Romain Francois a ?crit :
Le 19/03/10 21:26, Douglas Bates a ?crit :
By the way, I'm sorry for the misleading subject line in my original post. ?When I started the message I was trying to extract one of those functions in the "family" list and create a call. On Fri, Mar 19, 2010 at 3:20 PM, Romain Francois <romain.francois at dbmail.com> ? wrote:
Hello, The following works for me : require( Rcpp ) require( inline ) fx<- cfunction( signature( data_ = "list" ), ' List data(data_) ; std::string family = data["family"] ; std::string link ? = data["link"] ; std::cout<< ? "family :"<< ? family<< ? std::endl ; std::cout<< ? "link ? :"<< ? link<< ? std::endl ; return R_NilValue ; ', Rcpp = TRUE, includes = "using namespace Rcpp;" )
fx( )
family : poisson link ? : log NULL But we have an issue with the proxy class we use in CharacterVector. It seems if you have a CharacterVector : CharacterVector family = data["family"] ; you can grab one element as a C string (const char*) const char* fam = family[0] ; but not as a std::string for some reason. some investigation needed.
I think the underlying question for me is why the Rcpp::Vector template does not take both the element type and the Rtype as arguments. ?Having the () and [] operators in the template return a Proxy type may be necessary but I don't know enough about templates to decide why. ?If I have instantiated the NumericVector class from the template as wrapping a REALSXP then the only thing I can imagine the () and [] operators returning is a double.
Yes. For simple vectors (integer, raw, logical, numeric, complex), the
indexers return the obvious type (int, Rbyte, int, double, Rcomplex).
For other vector types, it is more complex and we have to resort to
proxy classes. so List::operator[] returns an instance of some proxy
class, and the proxy class is supposed to handle anything you throw at it
:
- if the proxy is used on the lhs, an implcit call to wrap is made so
that what gets stored into the list is a SEXP, via SET_VECTOR_ELT. This
is why this works :
data["foo"] = 10 ;
data["foo"] creates a proxy and the proxy class has the magic templated
operator= :
template<typename T>
generic_name_proxy& ?operator=( const T& ?rhs ){
? ? ? ?set( ::Rcpp::wrap(rhs) ) ;
? ? ? ?return *this ;
}
used with T=int in this case, so anything that can be wrapped can be
assigned using this syntax in a list
- if the proxy is used on the rhs, an implicit call to as is made to
attempt to convert what is currently at the given position (which is a
SEXP) to whatever is requested. So in:
CharacterVector family = data["family"] ;
data["family"] creates a proxy and the proxy has the magic implicit
conversion operator :
template<typename T>
operator T(){
? ? ? ?return ::Rcpp::as<T>( get() ) ;
}
so that in this case T is CharacterVector
In this instance the proxy class is generic_name_proxy if you want to
have a look in the code, it is "generic_proxy" if what is inside the
brackets is an int.
We seem to have an issue with the proxies that are used with
CharacterVector, and I'll have a look tomorrow.
Romain
Part of the problem for me is what to do with the result of () or [] from an Rcpp::List. ?I can't manage to convince the compiler that it's some kind of Rcpp::RObject, which I would say it has to be.
Romain Le 19/03/10 20:21, Douglas Bates a ?crit :
I must be missing something horribly obvious but I have now spent several hours trying to find a way to extract a character string from a list. ?I am more-or-less certain that I am copying a construction in the unitTests/runit.CharacterVector.R but the compiler just keeps spitting out error messages at me. The background is that a glm "family" object in R is a named list (or should be, it has an S3 class but that doesn't mean anything about the structure). ?The standard ones look like
str(poisson())
List of 12
? $ family ? ?: chr "poisson"
? $ link ? ? ?: chr "log"
? $ linkfun ? :function (mu)
? $ linkinv ? :function (eta)
? $ variance ?:function (mu)
? $ dev.resids:function (y, mu, wt)
? $ aic ? ? ? :function (y, n, mu, wt, dev)
? $ mu.eta ? ?:function (eta)
? $ initialize: ?expression({ ? ? if (any(y< ? ? 0))
stop("negative values not allowed for the Poisson family") ? ? n<-
rep.int(1, nobs) ? ? mustart<- y + 0.1 })
? $ validmu ? :function (mu)
? $ valideta ?:function (eta)
? $ simulate ?:function (object, nsim)
? - attr(*, "class")= chr "family"
All I want to do is to take such a list and extract the "family" and
"link" strings as std::string. ?If the name of the Rcpp::List object
in the C++ code is lst, I can get as far as
StringVector fam = lst["family"];
but after that it all turns to custard. ?In the class I am defining
the family member is declared as a std::string. ?In the constructor
for the class from a List object I try
family = Rcpp::as<std::string>(fam[0]);
or even, mimicking a couple of the tests in
unitTests/runit.CharacterVector.R,
family += fam[0];
but I have not been able to construct anything that my compiler will
accept.
So, how do I get the value of one of the elements of a CharacterVector
(or StringVector, I think they are synonyms) as a std::string, short
of using std::string(CHAR(STRING_ELT(fam, 0))
-- Romain Francois Professional R Enthusiast +33(0) 6 28 91 30 30 http://romainfrancois.blog.free.fr |- http://tr.im/OIXN : raster images and RImageJ |- http://tr.im/OcQe : Rcpp 0.7.7 `- http://tr.im/O1wO : highlight 0.1-5