[Rcpp-devel] Rcpp modules : help with the design
Romain Francois
romain at r-enthusiasts.com
Mon May 31 20:07:37 CEST 2010
Thanks John,
Le 31/05/10 18:23, John Chambers a écrit :
> Just a couple of comments.
>
> 1. class names
> One result of creating an S4 class with the same name as the C++ class
> is that you may be hiding another S4 class of that name. Always possible
> in any application, but when dealing with an inter-system interface it
> may be more likely and more of a problem. You might, at least as an
> option, make the class name involve the module name as well:
> "yada_World" or something along that line.
I have mixed feelings about this. I'm not sure I'd like writing code
that looks like this :
setMethod( "foo", signature( x = "yada__World", y = "yada__Foo"), ... )
But this is of course a real problem. One thing is that it is the user
who decides the R class name, so the example below could have been :
class_<World>( "SomethingElse" )
I guess we could add a "prefix" option in Module so that it would prefix
all class names with it.
One other thing is that when we do :
yada <- Module( "yada" )
World <- yada$World
w <- new( World )
there are no quotes around "World", i.e. we have a generic "new" (the
idea was borrowed from rJava). I was wondering if something like this
would work for setMethod, e.g. something like :
setMethod( "foo" , signature( x = World, y = Foo ), .... )
where World and Foo are objects, not strings. We could probably easily do :
setMethod( "foo", World, ... )
but I'm not sure this would work with signature, etc ... and if this is
a good idea at all.
One other thing, when we load the module with the Module function, it
sets "where" to the top environment of the parent frame (same as what
setClass does), so if Module is called from a package namespace, classes
are defined inside the namespace. not sure it plays a role in a
potential dispatch clash.
> 2. inheritance.
> If the exposure of the C++ class were to include its C++-side
> inheritance, it might be useful for the implied S4 classes to have
> analogous inheritance, but that information would need to be in the module.
We currently don't deal with C++ level inheritance. The model
(Boost.Python) does though so it is likely that we'll want that at some
point.
When we figure it out, I totally agree that the R level should be aware
of the inheritance and S4 inheritance is perfect for that.
Romain
> John
>
>
> On 5/29/10 1:25 AM, Romain Francois wrote:
>> 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/99bz5D : highlight 0.1-9
|- http://bit.ly/9CQ66r : RMetrics 2010
`- http://bit.ly/bklUXt : RcppArmadillo 0.2.1
More information about the Rcpp-devel
mailing list