[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