[Rcpp-devel] Rcpp modules : help with the design

Romain Francois romain at r-enthusiasts.com
Fri Jun 4 14:13:19 CEST 2010


Le 31/05/10 20:07, Romain Francois a écrit :
>
> 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"), ... )

Perhaps one idea would be this:

setGeneric( "::" )
setMethod( "::", "Module", function( pkg, name ){
	sprintf( "%s__%s", pkg at prefix, name )
} )

so that we can do something like:

setMethod( "foo", signature( x = yada::World , y = yada::Foo ), ... )

I think I'd prefer typing this over:

setMethod( "foo", signature( x = "yada__World", y = "yada__Foo"), ... )

But it may be too confusing ?

Romain

> 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/c6YnCi : graph gallery collage
|- http://bit.ly/bZ7ltC : inline 0.3.5
`- http://bit.ly/8YUsiC : highlight 0.2-0



More information about the Rcpp-devel mailing list