[Rcpp-devel] Idiom for accessing scalars

Romain François romain at r-enthusiasts.com
Sat Jan 7 19:02:43 CET 2012


Le 07/01/12 18:55, Dirk Eddelbuettel a écrit :
> On 7 January 2012 at 18:40, Romain François wrote:
> | Le 07/01/12 18:36, Douglas Bates a écrit :
> |>  On Sat, Jan 7, 2012 at 10:55 AM, Dirk Eddelbuettel<edd at debian.org>   wrote:
> |>>  On 7 January 2012 at 10:04, Douglas Bates wrote:
> |>>  | 2012/1/7 Romain François<romain at r-enthusiasts.com>:
> |>>  |>   Le 06/01/12 20:46, Douglas Bates a écrit :
> |>>  |>
> |>>  |>>   On Fri, Jan 6, 2012 at 1:12 PM, Dirk Eddelbuettel<edd at debian.org>     wrote:
> |>>  |>>>
> |>>  |>>>   On 6 January 2012 at 12:59, Douglas Bates wrote:
> |>>  |>>>   | On Fri, Jan 6, 2012 at 12:39 PM, John Chambers<jmc at stat.stanford.edu>
> |>>  |>>>     wrote:
> |>>  |>>>   |>     The "Rf_" part of the API in particular is ugly and somewhat of an
> |>>  |>>>   add-on
> |>>  |>>>   |>     forced in a few examples by the use of some common names in the macro
> |>>  |>>>   files.
> |>>  |>>>   |
> |>>  |>>>   | But, as it stands, that is a requirement when using Rcpp.
> |>>  |>>>
> |>>  |>>>   Where?  I can think of one propagated use, which is at the bottom of the
> |>>  |>>>   try/catch structure where we use ::Rf_error.  But the commonly used
> |>>  |>>>   macros
> |>>  |>>>   hide it, and we could/should obviously wrap this.
> |>>  |>>>
> |>>  |>>>   Otherwise, and especially since the 'Rcpp sugar' initiative took off, I
> |>>  |>>>   don't
> |>>  |>>>   really touch any ::Rf_* myself anymore.  Inside the Rcpp code base, sure.
> |>>  |>>>   But
> |>>  |>>>   not really in user-facing stuff and Rcpp applications.
> |>>  |>>
> |>>  |>>   I didn't make myself clear.  What I meant was that it is not possible
> |>>  |>>   to use asInteger in Rcpp and count on the name being remapped to
> |>>  |>>   Rf_asInteger.
> |>>  |>>
> |>>  |>>>   | I think of the Rf_ part as more due to the fact that C doesn't have a
> |>>  |>>>   | concept of namespaces so anything in the R API is at the top level
> |>>  |>>>   | namespace leading to some conflicts.
> |>>  |>>>
> |>>  |>>>   Agreed.  But speaking stylistically, for the same reason that we prefer
> |>>  |>>>   C++
> |>>  |>>>   versions of C header files (eg cstdint over stdint.h, cstdio over
> |>>  |>>>   stdio.h,
> |>>  |>>>   ...) I am with John on the preference for C++ idioms when given a choice.
> |>>  |>>
> |>>  |>>   I suppose I could have just checked whether Rcpp::as<int>     calls
> |>>  |>>   Rf_asInteger.  If so, everything is cool.  Unfortunately, I haven't
> |>>  |>>   been able to find that specialization.
> |>>  |>>
> |>>  |>   as lives in the inst/include/Rcpp/as.h file, and we have to follow template
> |>>  |>   wizardry:
> |>>  |>
> |>>  |>   it starts from :
> |>>  |>
> |>>  |>   template<typename T>   T as( SEXP m_sexp) {
> |>>  |>           return internal::as<T>( m_sexp, typename
> |>>  |>   traits::r_type_traits<T>::r_category() ) ;
> |>>  |>       }
> |>>  |>
> |>>  |>   with T=int, so we end up calling this one:
> |>>  |>
> |>>  |>   template<typename T>   T as( SEXP x, ::Rcpp::traits::r_type_primitive_tag ) {
> |>>  |>               if( ::Rf_length(x) != 1 ) throw ::Rcpp::not_compatible(
> |>>  |>   "expecting a single value" ) ;
> |>>  |>               const int RTYPE = ::Rcpp::traits::r_sexptype_traits<T>::rtype ;
> |>>  |>               SEXP y = PROTECT( r_cast<RTYPE>(x) );
> |>>  |>               typedef typename ::Rcpp::traits::storage_type<RTYPE>::type
> |>>  |>   STORAGE;
> |>>  |>               T res = caster<STORAGE,T>( *r_vector_start<RTYPE,STORAGE>( y ) )
> |>>  |>   ;
> |>>  |>               UNPROTECT(1) ;
> |>>  |>               return res ;
> |>>  |>           }
> |>>  |>
> |>>  |>
> |>>  |>   which does the magic. There is no calls to asInteger.
> |>>  |
> |>>  | Which, to me, is the disadvantage.  The asInteger function is brief,
> |>>  | understandable, flexible and well-tested.  This may look transparent
> |>>  | to you but not to many others.
> |>>
> |>>  It is however templated and valid for more types than just integer--it covers
> |>>  everything mapped by the r_type_primitive type so that the caster can be
> |>>  invoked.
> |>  You got it backwards, Dirk.  The asInteger function converts any kind
> |>  of SEXP that it can make sense of to an integer scalar so it is fully
> |>  general, which is why I prefer it.
> |>
> |>  int asInteger(SEXP x)
> |>  {
> |>       int warn = 0, res;
> |>
> |>       if (isVectorAtomic(x)&&   LENGTH(x)>= 1) {
> |>  	switch (TYPEOF(x)) {
> |>  	case LGLSXP:
> |>  	return IntegerFromLogical(LOGICAL(x)[0],&warn);
> |>  	case INTSXP:
> |>  	return INTEGER(x)[0];
> |>  	case REALSXP:
> |>  	res = IntegerFromReal(REAL(x)[0],&warn);
> |>  	CoercionWarning(warn);
> |>  	return res;
> |>  	case CPLXSXP:
> |>  	res = IntegerFromComplex(COMPLEX(x)[0],&warn);
> |>  	CoercionWarning(warn);
> |>  	return res;
> |>  	case STRSXP:
> |>  	res = IntegerFromString(STRING_ELT(x, 0),&warn);
> |>  	CoercionWarning(warn);
> |>  	return res;
> |>  	default:
> |>  	UNIMPLEMENTED_TYPE("asInteger", x);
> |>  	}
> |>       } else if(TYPEOF(x) == CHARSXP) {
> |>  	res = IntegerFromString(x,&warn);
> |>  	CoercionWarning(warn);
> |>  	return res;
> |>       }
> |>       return NA_INTEGER;
> |>  }
> | Right. I'm sold. I'll have a look at how to squeeze it in.
>
> I am a bit perplexed. The as<>  template is (if I may abuse database lingo)
> "many to many": a SEXP contained an opaque type get matched to one of several
> R base types, using template magic. But asInteger is only "many-to-one" as it
> can return int only, given the opaque SEXP.
>
> We gain about a nanosecond per invocation as per the microbenchmark we
> posted.  Is that really worth a rewrite?  I don't care either way, I just
> think we may have other open tickets...
>
> Dirk
Not thinking about a rewrite, just adding e.g. this for int:

template <> inline int as<int>( SEXP x, 
::Rcpp::traits::r_type_primitive_tag ) {
            return Rf_asInteger( x ) ;
         }

so that we let R's asInteger do the actual work, and Doug is right, it 
does a better job than the current emplated version.

For example this works:

 > fx <- cxxfunction( signature(x_="ANY"), ' int x = as<int>( x_ ); 
return wrap(x); ', plugin="Rcpp")
 >
 > fx( "123" )
[1] 123



However,

 > fx( c(1L, 3L ) )
[1] 1

this passes, where as the current version complains that it should be a 
single value.


-- 
Romain Francois
Professional R Enthusiast
http://romainfrancois.blog.free.fr



More information about the Rcpp-devel mailing list