[Rcpp-devel] Rcpp modules : help with the design
Romain Francois
romain at r-enthusiasts.com
Sat May 29 10:25:34 CEST 2010
Le 29/05/10 09:10, Romain Francois a écrit :
>
> Hello,
>
> Currently, when exposing a c++ class to R through a Rcpp module, e.g:
>
> class World {
> public:
> World() : msg("hello"){}
> void set(std::string msg) { this->msg = msg; }
> std::string greet() { return msg; }
>
> private:
> std::string msg;
> };
>
> void clearWorld( World* w){
> w->set( "" ) ;
> }
>
> RCPP_MODULE(yada){
> using namespace Rcpp ;
>
> class_<World>( "World" )
> .method( "greet", &World::greet )
> .method( "set", &World::set )
> .method( "clear", &clearWorld )
> ;
> }
>
>
>
> On the R side, we do :
>
> # grab the module and the class
> yada <- Module( "yada" )
> World <- yada$World
>
> # create an instance and play with it
> w <- new( World )
> w$greet()
>
>
> The "w" object is of class "C++Object":
>
> > class( w )
> [1] "C++Object"
> attr(,"package")
> [1] "Rcpp"
>
> > str( w )
> Formal class 'C++Object' [package "Rcpp"] with 3 slots
> ..@ module :<externalptr>
> ..@ cppclass:<externalptr>
> ..@ pointer :<externalptr>
>
> The "C++Object" class contains all the information that is needed
> internally to dispatch to the internal method : pointer to its module,
> pointer to its class wrapper and pointer to the actual object.
>
>
>
> However, I think one of the next step would be for the user to define S4
> methods for C++ classes, i.e it would be nice to be able to do something
> like:
>
> setMethod( "show", "World", function( object ){
> writeLines( object$greet() )
> } )
>
> but right now we cannot because there is no S4 class "World", all c++
> objects share the R class "C++Object".
>
> This is the context of this question. I need help for this.
>
> Options I am thinking about:
>
> 1) when the module is loaded, we define a "World" class that extends
> "C++Object", i.e :
>
> setClass( "World", contains = "C++Object" )
>
> this would probably be the most natural way for the user. I am not sure
> where to store that class. should I create a new environment for the
> module, ...
>
>
> 2) make the dispatch internal, something like:
>
> setInternalMethod( "show", World, function(object){
> writeLines( object$greet() )
> } )
>
> so that :
> - it registers an S4 "show" method for the "C++Object" class
> - we maintain internally some sort of lookup table to find the show
> method for the internal class.
>
>
>
>
> We have the same problem in rJava where all java references have the
> class "jobjRef", which makes it hard to do R side dispatch based on the
> java class. I've used something like option 2 with rJava and the RImageJ
> package once but I think option 1 is better and more natural.
>
> It is probably more difficult to achieve option 1 with rJava because
> since java has reflection, we get all classes for free, where we need to
> specifically declare which ones with expose with Rcpp modules.
>
> Anyway, I'm not asking for help in the C++ side, I realize this is
> crazy. But some R-level help would be welcome.
>
> Romain
Hi,
Few cup of coffee later, I have implemented option 1) :
######################################################################
require( Rcpp )
require( inline )
code <- ''
inc <- '
class World {
public:
World() : msg("hello"){}
void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
private:
std::string msg;
};
void clearWorld( World* w){
w->set( "" ) ;
}
RCPP_MODULE(yada){
using namespace Rcpp ;
class_<World>( "World" )
.method( "greet", &World::greet )
.method( "set", &World::set )
.method( "clear", &clearWorld )
;
}
'
fx <- cxxfunction( signature(), "", include = inc, plugin = "Rcpp" )
yada <- Rcpp:::Module( "yada", getDynLib( fx ) )
# grab the World class
World <- yada$World
# create a new World object
w <- new( World )
setMethod( "show", "World", function(object){
msg <- object$greet()
writeLines( paste( "message from World:", msg ) )
} )
######################################################################
> w
message from World: hello
When the module is loaded (with the Module function), it registers the
classes as simple extensions of the S4 class "C++Object" :
> str( getClass( "C++Object" ) )
Formal class 'classRepresentation' [package "methods"] with 11 slots
..@ slots :List of 3
.. ..$ module : atomic [1:1] externalptr
.. .. ..- attr(*, "package")= chr "methods"
.. ..$ cppclass: atomic [1:1] externalptr
.. .. ..- attr(*, "package")= chr "methods"
.. ..$ pointer : atomic [1:1] externalptr
.. .. ..- attr(*, "package")= chr "methods"
..@ contains : list()
..@ virtual : logi FALSE
..@ prototype :Formal class 'S4' [package ""] with 0 slots
list()
..@ validity : NULL
..@ access : list()
..@ className : atomic [1:1] C++Object
.. ..- attr(*, "package")= chr "Rcpp"
..@ package : chr "Rcpp"
..@ subclasses:List of 1
.. ..$ World:Formal class 'SClassExtension' [package "methods"] with
10 slots
.. .. .. ..@ subClass : chr "World"
.. .. .. ..@ superClass: chr "C++Object"
.. .. .. ..@ package : chr ".GlobalEnv"
.. .. .. ..@ coerce :function (from, strict = TRUE)
.. .. .. ..@ test :function (object)
.. .. .. ..@ replace :function (from, to, value)
.. .. .. ..@ simple : logi TRUE
.. .. .. ..@ by : chr(0)
.. .. .. ..@ dataPart : logi FALSE
.. .. .. ..@ distance : num 1
..@ versionKey:<externalptr>
..@ sealed : logi FALSE
This way we can have both:
- internal dispatch of the traditional OO form : object$method( ... )
- S4 dispatch : generic( object, ... )
My next move is probably going to be melting both, so that we can define
classic methods such as show internally, something like:
class_<World>( "World" )
.method( "greet", &World::greet )
.method( "set", &World::set )
.method( "clear", &clearWorld )
.S4_method( "show", &showWorld )
;
Romain
--
Romain Francois
Professional R Enthusiast
+33(0) 6 28 91 30 30
http://romainfrancois.blog.free.fr
|- http://bit.ly/9CQ66r : RMetrics 2010
|- http://bit.ly/cork4b : highlight 0.1-8
`- http://bit.ly/bklUXt : RcppArmadillo 0.2.1
More information about the Rcpp-devel
mailing list