[Rcpp-devel] Idiom for accessing scalars

Dirk Eddelbuettel edd at debian.org
Sat Jan 7 18:55:48 CET 2012


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


-- 
"Outside of a dog, a book is a man's best friend. Inside of a dog, it is too
dark to read." -- Groucho Marx


More information about the Rcpp-devel mailing list