Skip to content

[Rcpp-devel] Exposing constructors with same number of arguments with different types using Rcpp Modules

7 messages · Dirk Eddelbuettel, Jelmer Ypma, Romain Francois

#
Dear Rcpp-list,

I'm trying to expose multiple constructors of a C++ class to R using
modules. Here is an example of what I want to do based on the Uniform
example

===Begin: example
library('inline')
library('Rcpp')

test_code <-'
using namespace Rcpp;

class Uniform {
    public:
        Uniform(double min_, double max_) : min(min_), max(max_) {}
        Uniform(double max_, std::string dummy_) : min(0.0), max(max_)
{ Rprintf("%s\\n", dummy_.c_str()); }
        Uniform(double max_ ) : min(0.0), max(max_) {}

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

        double min, max;
};

double range( Uniform* w) {
    return w->max - w->min;
}

RCPP_MODULE(unif_module) {
    class_<Uniform>( "Uniform" )

    .constructor<double,double>()
    .constructor<double,std::string>()
    .constructor<double>()

    .field( "min", &Uniform::min )
    .field( "max", &Uniform::max )

    .method( "draw", &Uniform::draw )
    .method( "range", &range )
    ;
}
'

fx <- cxxfunction( signature(), "" , include = test_code, plugin = "Rcpp" )
unif_module <- Module( "unif_module", getDynLib(fx) )

show( Uniform )
u1 <- new( Uniform, 0, 10 )
u1$min
u1$max
u1$range()
u1$draw( 10L )

u2 <- new( Uniform, 10, "test" )
u2$min
u2$max
u2$range()
u2$draw( 10L )

u3 <- new( Uniform, 10 )
u3$min
u3$max
u3$range()
u3$draw( 10L )
===End: example

Compilation works fine (on Windows using RTools, Rcpp_0.9.4.1,
inline_0.3.8), but the R code cannot distinguish between two
constructors with the same number of arguments, but with different
types for the arguments and always calls Uniform(double, double). The
output I get is as follows:
C++ class 'Uniform' <02748CF0>
Constructors:
    Uniform(double, double)
    Uniform(double, std::string)
    Uniform(double)

Fields:
    double max
    double min

Methods:
     Rcpp::NumericVector draw(int)  const

     double range()
[1] 0
[1] 10
[1] 10
[1] 6.330045 4.637002 6.507183 4.192280 9.560602 3.927548 4.399107
2.332956 8.810553 3.864929
Error in new_CppObject_xp(fields$.module, fields$.pointer, ...) :
  not compatible with REALSXP


Does anyone know of a workaround for this?

Many thanks in advance!
Jelmer
#
Hi Jelmer,
On 13 May 2011 at 16:35, Jelmer Ypma wrote:
| Dear Rcpp-list,
| 
| I'm trying to expose multiple constructors of a C++ class to R using
| modules. Here is an example of what I want to do based on the Uniform
| example
| 
| ===Begin: example
| library('inline')
| library('Rcpp')
| 
| test_code <-'
| using namespace Rcpp;
| 
| class Uniform {
|     public:
|         Uniform(double min_, double max_) : min(min_), max(max_) {}
|         Uniform(double max_, std::string dummy_) : min(0.0), max(max_)
| { Rprintf("%s\\n", dummy_.c_str()); }
|         Uniform(double max_ ) : min(0.0), max(max_) {}
| 
|         NumericVector draw(int n) const {
|             RNGScope scope;
|             return runif( n, min, max );
|         }
| 
|         double min, max;
| };
| 
| double range( Uniform* w) {
|     return w->max - w->min;
| }
| 
| RCPP_MODULE(unif_module) {
|     class_<Uniform>( "Uniform" )
| 
|     .constructor<double,double>()
|     .constructor<double,std::string>()
|     .constructor<double>()
| 
|     .field( "min", &Uniform::min )
|     .field( "max", &Uniform::max )
| 
|     .method( "draw", &Uniform::draw )
|     .method( "range", &range )
|     ;
| }
| '
| 
| fx <- cxxfunction( signature(), "" , include = test_code, plugin = "Rcpp" )
| unif_module <- Module( "unif_module", getDynLib(fx) )
| 
| show( Uniform )
| u1 <- new( Uniform, 0, 10 )
| u1$min
| u1$max
| u1$range()
| u1$draw( 10L )
| 
| u2 <- new( Uniform, 10, "test" )
| u2$min
| u2$max
| u2$range()
| u2$draw( 10L )
| 
| u3 <- new( Uniform, 10 )
| u3$min
| u3$max
| u3$range()
| u3$draw( 10L )
| ===End: example
| 
| Compilation works fine (on Windows using RTools, Rcpp_0.9.4.1,
| inline_0.3.8), but the R code cannot distinguish between two
| constructors with the same number of arguments, but with different
| types for the arguments and always calls Uniform(double, double). The

If I recall correctly, we have determined that this cannot work. It is one of
these 'obvious in hindsight' issue.  Recall that R does the dispatch, and R
cannot differentiate between  foo(double) and  foo(int)  as it is not typed.

So I think you will have to differentiate the constructors by name. Sorry
about that, but even Rcpp modules has its limits.

And I may of course be wrong too in which case Romain may come in and
clarify.

Hope this helps,  Dirk

| output I get is as follows:
| 
| > unif_module <- Module( "unif_module", getDynLib(fx) )
| 
| > show( Uniform )
| C++ class 'Uniform' <02748CF0>
| Constructors:
|     Uniform(double, double)
|     Uniform(double, std::string)
|     Uniform(double)
| 
| Fields:
|     double max
|     double min
| 
| Methods:
|      Rcpp::NumericVector draw(int)  const
| 
|      double range()
| 
| 
| > u1 <- new( Uniform, 0, 10 )
| 
| > u1$min
| [1] 0
| 
| > u1$max
| [1] 10
| 
| > u1$range()
| [1] 10
| 
| > u1$draw( 10L )
|  [1] 6.330045 4.637002 6.507183 4.192280 9.560602 3.927548 4.399107
| 2.332956 8.810553 3.864929
| 
| > u2 <- new( Uniform, 10, "test" )
| Error in new_CppObject_xp(fields$.module, fields$.pointer, ...) :
|   not compatible with REALSXP
| 
| 
| Does anyone know of a workaround for this?
| 
| Many thanks in advance!
| Jelmer
| _______________________________________________
| Rcpp-devel mailing list
| Rcpp-devel at lists.r-forge.r-project.org
| https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
#
Hi Dirk,

many thanks for your quick reply. Maybe something like the below could
work? The different constructor specifications are defined in the list
ClassName at constructors, but I coudln't find where R decides which of
these functions to call.

Thanks again,
Jelmer

overloaded.func <- function( x1, x2 ) {
    f1 <- function( x1, x2 ) {
        cat( "f1\n" )
        return( x1 + x2 )
    }

    f2 <- function( x1, x2 ) {
        cat( "f2\n" )
        return( nchar(x1) + nchar(x2) )
    }

    f3 <- function( x1, x2 ) {
        cat( "f3\n" )
        return( x1 + nchar(x2) )
    }

    f4 <- function( x1, x2 ) {
        cat( "f4\n" )
        return( x1 + x2 )
    }

    # add different versions to list
    func.list <- list( f1, f2, f3, f4 )

    # define argument types of f1, f2, f3
    argstypes <- list(
        c( "numeric", "numeric" ),          # input arguments of f1
        c( "character", "character" ),      # input arguments of f2
        c( "numeric", "character" ),        # input arguments of f3
        c( "numeric", "integer" ) )         # input arguments of f4

    # get types of input arguments supplied by user
    inputtypes <- c( class( x1 ), class( x2 ) )

    # get index of function that agrees with these arguments
    findex <- which( sapply( argstypes, function(type) {
all(inputtypes == type) } ) )

    # if there is such a function evalute it, otherwise return error
    if( length( findex ) == 0 ) {
        stop(paste( inputtypes, sep=' '  ))
    }
    else {
        return( func.list[[ findex ]]( x1, x2 ) )
    }
}


overloaded.func( 3, 3 )             # f1
overloaded.func( "test", "test" )   # f2
overloaded.func( 3, "test" )        # f3
overloaded.func( 2, 3L )            # f4
overloaded.func( "test", 3 )        # error
On Fri, May 13, 2011 at 18:58, Dirk Eddelbuettel <edd at debian.org> wrote:
#
On 13 May 2011 at 11:58, Dirk Eddelbuettel wrote:
| On 13 May 2011 at 16:35, Jelmer Ypma wrote:
| | Dear Rcpp-list,
| | 
| | I'm trying to expose multiple constructors of a C++ class to R using
| | modules. Here is an example of what I want to do based on the Uniform
| | example

[...]

| | Compilation works fine (on Windows using RTools, Rcpp_0.9.4.1,
| | inline_0.3.8), but the R code cannot distinguish between two
| | constructors with the same number of arguments, but with different
| | types for the arguments and always calls Uniform(double, double). The
| 
| If I recall correctly, we have determined that this cannot work. It is one of
| these 'obvious in hindsight' issue.  Recall that R does the dispatch, and R
| cannot differentiate between  foo(double) and  foo(int)  as it is not typed.
| 
| So I think you will have to differentiate the constructors by name. Sorry
| about that, but even Rcpp modules has its limits.
| 
| And I may of course be wrong too in which case Romain may come in and
| clarify.

I got a brief word in with Romain who clarified that this ought to be
possible but may not be right now.  So stay tuned -- with some luck Romain
may be able to improve on what we currently have.

In the meantime you may have to make do with different setter functions.

Dirk
#
Excellent, thanks for the update.

Jelmer
On Sat, May 14, 2011 at 17:20, Dirk Eddelbuettel <edd at debian.org> wrote:
1 day later
#
There is a mechanism for dispatching to the appropriate constructor, but 
this needs extra work from you on the C++ side. I'm currently not 
entirely happy about it, and this might change in the future for 
something nicer.

Some information about it was posted on this thread:
http://article.gmane.org/gmane.comp.lang.r.rcpp/929/match=constructors

You essentially have to supply an extra function that decides if the 
constructor is appropriate for the arguments that are passed in:

So your example would become something like this:

library('inline')
library('Rcpp')

test_code <-'
using namespace Rcpp;

class Uniform {
     public:
         Uniform(double min_, double max_) : min(min_), max(max_) {}
         Uniform(double max_, std::string dummy_) : min(0.0), max(max_){
             Rprintf("%s\\n", dummy_.c_str());
         }
         Uniform(double max_ ) : min(0.0), max(max_) {}

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

         double min, max;
};

double range( Uniform* w) {
     return w->max - w->min;
}

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

RCPP_MODULE(unif_module) {
     class_<Uniform>( "Uniform" )

     .constructor<double,double>("", &fun1 )
     .constructor<double,std::string>("", &fun2)
     .constructor<double>()

     .field( "min", &Uniform::min )
     .field( "max", &Uniform::max )

     .method( "draw", &Uniform::draw )
     .method( "range", &range )
     ;
}
'

fx <- cxxfunction( signature(), "" , include = test_code, plugin = "Rcpp" )
unif_module <- Module( "unif_module", getDynLib(fx) )
Uniform <- unif_module$Uniform

u1 <- new( Uniform, 0, 10 )
u1$min
u1$max
u1$range()
u1$draw( 10L )

u2 <- new( Uniform, 10, "test" )
u2$min
u2$max
u2$range()
u2$draw( 10L )

u3 <- new( Uniform, 10 )
u3$min
u3$max
u3$range()
u3$draw( 10L )


Romain


Le 13/05/11 16:35, Jelmer Ypma a ?crit :

  
    
#
Thanks, this works for me.

Jelmer
On Mon, May 16, 2011 at 10:33, Romain Francois <romain at r-enthusiasts.com> wrote: