|
Rcpp and the new R:: namespace for Rmath.h
We released Rcpp 0.10.0 earlier today. This post will just provide a
simple example for one of the smaller new features -- the new namespace for functions from Rmath.h -- and illustrate one of the
key features (Rcpp attributes) in passing.
R, as a statistical language and environment, has very well written and tested statistical distribution functions providing
probability density, cumulative distribution, quantiles and random number draws for dozens of common and not so common distribution functions.
This code is used inside R, and available for use from standalone C or C++ programs via the standalone R math library which Debian /
Ubuntu have as a package r-mathlib (and which can be built from R sources).
User sometimes write code against this interface, and then want to combine the code with other code, possibly even with Rcpp. We allowed
for this, but it required a bit of an ugly interface. R provides a C interface; these have no namespaces. Identifiers can clash, and to be
safe one can enable a generic prefix Rf_. So functions which could clash such as length or error
become Rf_length and Rf_error and are less likely to conflict with symbols from other libraries. Unfortunately, the side-effect
is that calling, say, the probability distribution function for the Normal distribution becomes Rf_pnorm5() (with the 5
denoting the five parameters: quantile, mean, std.deviation, lowerTail, logValue). Not pretty, and not obvious.
So one of the things we added was another layer of indirection by adding a namespace R with a bunch of inline'd wrapper
functions (as well as several handful of unit tests to make sure we avoided typos and argument transposition and what not).
The short example below shows this for a simple function taking a vector, and returning its pnorm computed three different ways:
#include <Rcpp.h>
// [[Rcpp::export]]
Rcpp::DataFrame mypnorm(Rcpp::NumericVector x) {
int n = x.size();
Rcpp::NumericVector y1(n), y2(n), y3(n);
for (int i=0; i<n; i++) {
// the way we used to do this
y1[i] = ::Rf_pnorm5(x[i], 0.0, 1.0, 1, 0);
// the way we can do it now
y2[i] = R::pnorm(x[i], 0.0, 1.0, 1, 0);
}
// or using Rcpp sugar in one go
y3 = Rcpp::pnorm(x);
return Rcpp::DataFrame::create(Rcpp::Named("Rold") = y1,
Rcpp::Named("Rnew") = y2,
Rcpp::Named("sugar") = y3);
}
This example also uses the new Rcpp attributes described briefly in the announcement blog post and of course in more detail in the
corresponding vignette. Let us just state here that we simply provide a complete C++ function, using standard Rcpp types -- along with one
'attribute' declaration of an export via Rcpp. That's it -- even easier than using inline.
Now in R we simply do
R> sourceCpp("mypnorm.cpp")
to obtain a callable R function with the C++ code just shown behind it. No Makefile, no command-line tool invocation -- nothing but a single
call to sourceCpp() which takes care of things --- and brings us a compiled C++ function to R just given the source file with
its attribute declaration.
We can now use the new function to compute the probaility distribution both the old way, the new way with the 'cleaner'
R::pnorm(), and of course the Rcpp sugar way in a single call. We build a data frame in C++, and assert that all
three variants are the same:
R> x <- seq(0, 1, length=1e3)
R> res <- mypnorm(x)
R> head(res)
Rold Rnew sugar
1 0.500000 0.500000 0.500000
2 0.500399 0.500399 0.500399
3 0.500799 0.500799 0.500799
4 0.501198 0.501198 0.501198
5 0.501597 0.501597 0.501597
6 0.501997 0.501997 0.501997
R> all.equal(res[,1], res[,2], res[,3])
[1] TRUE
R>
This example hopefully helped to illustrate how Rcpp 0.10.0 brings both something really powerful (Rcpp attributes -- more on this another
time, hopefully) and convenient in the new namespace for statistical functions.
/code/snippets |
permanent link
Rcpp 0.10.0
Rcpp release 0.10.0
is now on CRAN and being uploaded to Debian.
This is a new feature release, and we are very exciting about the
changes, notably Rcpp attributes which make using C++ from R even
easier than inline (see below as well as the
new
vignette for details and first examples), the extensions to Rcpp
modules (see below) and more as for example new Rcpp sugar
functions, a new error output device syncing to R, and a new namespace R>
for the statistical functions from Rmath.h.
The complete NEWS
entry for 0.10.0 is below; more details are in the ChangeLog file in the package and on the
Rcpp Changelog page.
Changes in Rcpp version 0.10.0 (2012-11-13)
Support for C++11 style attributes (embedded in comments) to enable use of C++ within interactive sessions and to automatically generate module declarations for packages:
Rcpp::export attribute to export a C++ function to R
sourceCpp() function to source exported functions from a file
cppFunction() and evalCpp() functions for inline declarations and execution
compileAttribtes() function to generate Rcpp modules from exported functions within a package
Rcpp::depends attribute for specifying additional build dependencies for sourceCpp()
Rcpp::interfaces attribute to specify the external bindings compileAttributes() should generate (defaults to R-only but a C++ include file using R_GetCCallable can also be generated)
New vignette "Rcpp-attribute"
Rcpp modules feature set has been expanded:
Functions and methods can now return objects from classes that are exposed through modules. This uses the make_new_object template internally. This feature requires that some class traits are declared to indicate Rcpp's wrap/as system that these classes are covered by modules. The macro RCPP_EXPOSED_CLASS and RCPP_EXPOSED_CLASS_NODECL can be used to declared these type traits.
Classes exposed through modules can also be used as parameters of exposed functions or methods.
Exposed classes can declare factories with ".factory". A factory is a c++ function that returns a pointer to the target class. It is assumed that these objects are allocated with new on the factory. On the R side, factories are called just like other constructors, with the "new" function. This feature allows an alternative way to construct objects.
"converter" can be used to declare a way to convert an object of a type to another type. This gets translated to the appropriate "as" method on the R side.
Inheritance. A class can now declare that it inherits from another class with the .derives<Parent>( "Parent" ) notation. As a result the exposed class gains methods and properties (fields) from its parent class.
New sugar functions:
-
which_min implements which.min. Traversing the sugar expression and returning the index of the first time the minimum value is found.
-
which_max idem
-
unique uses unordered_set to find unique values. In particular, the version for CharacterVector is found to be more efficient than R's version
-
sort_unique calculates unique values and then sorts them.
Improvements to output facilities:
Provide a namespace 'R' for the standalone Rmath library so that Rcpp users can access those functions too; also added unit tests
Development releases sets variable RunAllRcppTests to yes to run all tests (unless it was alredy set to 'no'); CRAN releases do not and still require setting – which helps with the desired CRAN default of less testing at the CRAN server farm.
Thanks to
CRANberries, you can also look at a
diff to the previous release 0.9.15.
As always, even fuller details are on the
Rcpp Changelog page and the
Rcpp page which also
leads to the downloads, the
browseable
doxygen docs and zip files of doxygen output for the standard formats.
A local directory has
source and documentation too.
Questions, comments etc should go to the
rcpp-devel mailing list
off the R-Forge page
Update: One link corrected.
/code/rcpp |
permanent link
|