[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