Hi,

As a follow up on yesterday's email. I've added some more code that allows multiple constructors to e registered with a simple mechanism for deciding which one is to be used.

The interface has changed, the second template parameter has disapeared and constructors are now registered through .constructor.

One important thing is that (for now at least), the default constructor is no more automatically registered, so one has to manually register it using .default_constructor.

The following code shows an example (with the typo from yesterday fixed as well ;-) )



require( Rcpp )
require( inline )
inc <- '

class Randomizer {
public:

    Randomizer() : min(0), max(1){}
    Randomizer( double min_, double max_) : min(min_), max(max_){}

        NumericVector get( int n ){
                RNGScope scope ;
                return runif( n, min, max );
        }

private:
        double min, max ;
} ;

RCPP_MODULE(mod){

        class_<Randomizer>( "Randomizer" )

            .default_constructor()
            .constructor( init_2<double,double>() )
        
                .method( "get" , &Randomizer::get ) ;

}
'

fx <- cxxfunction( , '', includes = inc, plugin = "Rcpp" )
mod <- Module( "mod", getDynLib( fx ) )
Randomizer <- mod$Randomizer
r <- new( Randomizer, 10.0, 20.0 ) ;
r$get(10)
r$get(5)

r <- new( Randomizer ) ;
r$get(10)
r$get(5)



So Randomizer has two constructors.
The default one is registered by default_constructor
The one that takes two double is registered by this line :

.constructor( init_2<double,double>() )




So how does this work. Internally I maintain a vector of constructors, and I invoke the first one that is "valid" for the supplied list of arguments.

What does "valid" mean is up to the developer, with a sensible default.

In addition to the signature definition ( init_2<double,double> ), one can also pass a function to .constructor as the second argument. This function must have the same signature as this one:

bool yes( SEXP* args, int nargs){
    return true ;
}

The default constructor is judged valid if no arguments are passed down, and a constructor taking 2 arguments is judged valid if two arguments are passed down, as generated by this function template:


template<int n>
bool yes_arity( SEXP* args, int nargs){
    return nargs == n ;
}

that is used in the default arguments of .constructor

    template <typename U0, typename U1>
self& constructor( init_2<U0,U1>, ValidConstructor valid = &yes_arity<2> )


Those are sensible defaults because most of the time people want to expose several constructors to deal with different arity.

But, the programmer can supply his own function, so for example if a constructor is only valid for a character vector of length two, one could pass this function to .constructor:


bool test( SEXP* args, int nargs ){
        if( nargs != 1 ) return false ;
        if( TYPEOF( args[0] ) != STRSXP ) return false ;
        if( LENGTH( args[0] ) != 2 ) return false ;
        return true ;
}

And do something like :

.constructor( init_1<CharacterVector>(), &test )

This might change again until Rcpp 0.8.9 is released. Suggestions are welcome. I might change "init_%d" to "signature_%d".

Romain

Le 04/11/10 11:29, Romain Francois a écrit :
Hello,

This question has come up a few times now. How can I use another
constructor than the default constructor in a class exposed by Rcpp
modules.

I've made a few changes to Rcpp to allow that. The new restriction is
that :
- we can only expose one constructor (we need to think about various
ways to implement dispatch)
- the constructor needs to have 0, 1 or 2 arguments. We will handle more
later, but I wanted people to test this first before launching a massive
copy/paste adventure. Also, the pattern is simple enough so that other
people can do it instead of me. (I can explain it a bit more if people
volunteer).

As an illustration, consider this simple class :

class Randomizer {
public:
Randomizer( double min_, double max_) : min(min), max(max_){}

NumericVector get( int n ){
RNGScope scope ;
return runif( n, min, max );
}

private:
double min, max ;
} ;

I can explain what it does, but I feel this is self explanatory.

What is relevant here is the constructor. There is no default
constructor (although the compiler might generate one on our behalf),
and we want to use the constructor that takes two doubles.

This is how we would do it (the syntax might change before Rcpp 0.8.9 is
out):

RCPP_MODULE(mod){

class_< Randomizer, init_2<double,double> >( "Randomizer" )
.method( "get" , &Randomizer::get ) ;

}

The relevant bit here is the second template argument of class_.

Full example below:


require( Rcpp )
require( inline )
inc <- '

class Randomizer {
public:
Randomizer( double min_, double max_) : min(min), max(max_){}

NumericVector get( int n ){
RNGScope scope ;
return runif( n, min, max );
}

private:
double min, max ;
} ;

RCPP_MODULE(mod){

class_< Randomizer, init_2<double,double> >( "Randomizer" )

.method( "get" , &Randomizer::get ) ;

}
'

fx <- cxxfunction( , '', includes = inc, plugin = "Rcpp" )
mod <- Module( "mod", getDynLib( fx ) )
Randomizer <- mod$Randomizer
r <- new( Randomizer, 10.0, 20.0 ) ;
r$get(10)
r$get(5)


Have fun !

Romain




--
Romain Francois
Professional R Enthusiast
+33(0) 6 28 91 30 30
http://romainfrancois.blog.free.fr
|- http://bit.ly/czHPM7 : Rcpp Google tech talk on youtube
|- http://bit.ly/9P0eF9 : Google slides
`- http://bit.ly/cVPjPe : Chicago R Meetup slides


_______________________________________________
Rcpp-devel mailing list
[email protected]
https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel

Reply via email to