[Rcpp-devel] Extract a function from a list and create a call

Douglas Bates bates at stat.wisc.edu
Mon Mar 22 03:12:01 CET 2010


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
>
>


More information about the Rcpp-devel mailing list