[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