Le 2013-07-22 10:12, Wush Wu a écrit :
Hi all,

I wrote a wrapper of hiredis, which is a minimalistic C client for the Redis database. Its name is `Rhiredis` and is much faster than rredis,
an existed redis client of R. Please
see http://rpubs.com/wush978/rhiredis [1] for details. 

Cool.

Thanks for the Rcpp, it is much easier to wrap a C library into R and
extend the features of R. 

Great. That's why we do it.

However, I still want to know if there is a better approach. I tried
three approaches. Here is my opinions.

# Preliminary

Given a 3rd party C library, it usually provides a C structure and
functions of memory management. For example:

```c

struct A {
    int flag;
    // ...
};

A* initA();
void freeA(A* a);
```

The structure `A` need to be stored into a R object and pass to
another function.
Also, the user might need to access `A.flag`. For simplicity, there is
only one field in this example. However, there might be a number of
fields in practice.

# XPtr

 Thanks for the user guide of Rcpp modules , this is the first
approach I tried.

We could expose `A` into R as follow:

```cpp
Rcpp::XPtr<A, freeA> a(initA());
```

However, we should write helper function to accessing `A.flag` for
every fields.

Yes. And at the R level you deal with an external pointer. One usually then have to maintain what they exactly mean in R.

# RCPP_MODULE

The guids of Rcpp modules also provides another approach:

```cpp

RCPP_EXPOSED_CLASS(A)

RCPP_MODULE(A) {
  class_<A>("A")
  .field("flag", &A::flag)
  ;
 }

//@export
//[[Rcpp::export]]
SEXP init() {
  BEGIN_RCPP
  return wrap(*initA());
  END_RCPP
}
```

This will produce a S4 class named `A` in R which stores C structure
`A`.

However, it also produces memory leak because no `freeA` is called.

Adding `.finalizer(freeA)` in `RCPP_MODULE` will cause an error of
freeing memory twice.

Gotta look into why this happens.

# Embed `A` into C++ class and expose the class with RCPP_MODULE

This approach is implemented in `Rhiredis`.

Finally, I still need to write helper function to expose the field of
`A`. But the user could access these flag in R with operator `$`.

Note that I still need a function to extract the pointer of `A` from
exposed S4 object:

```cpp

template<class T>
T* extract_ptr(SEXP s) {
 Rcpp::S4 s4(s);
 Rcpp::Environment env(s4);
 Rcpp::XPtr<T> xptr(env.get(".pointer"));
 return static_cast<T*>(R_ExternalPtrAddr(xptr));
}
```

Please give me some suggestion if you know a better or a different
approach.

I would essentially do what you do: use RCPP_MODULE to expose a C++ class so that the C++ class manages scoping : constructor, destructor, etc ...

class A_cpp {
public:
    A_cpp( ) : obj( initA() ){}
    ~A_cpp(){ freeA(obj); obj = NULL ; }

    int get_flag(){ return obj->flag ; }
    void set_flag( int x ){ obj->flag = x ; }

private:
    A* obj ;
} ;

RCPP_MODULE(Whatever){
    class_<A_cpp>( "A" )
        .constructor()
        .property( "flag", &A_cpp::get_flag, &A_cpp::set_flag )
    ;
}

Then in R:

a <- new( A )
a$flag
a$flag <- 12L
a$flag

I would probably do some tricks with macros to avoid writing boiler plate get_ and set_, something like this :

#define GET_SET(_type_,_name_)                  \
_type_ get_##_name_(){ return obj->_name_ ; }   \
void set_##_name_(_type_ x){ obj->_name_ = x ; }

so that you'd use:

GET_SET(int,flag)
...




With the devel version of Rcpp, you can use as to get hold of a reference (or a const reference) of your object from the SEXP:

int get_flag( SEXP s4obj ){
    A_cpp& obj = as<A_cpp&>( s4obj ) ;
    return obj.get_flag() ;
}

But you could also expose this simply as :

int get_flag( A_cpp& obj ){
    return obj.get_flag() ;
}

To have access to this "as", you have to declare some class traits, but the RCPP_EXPOSED_CLASS can help you with this.
Here is a working .cpp file that demonstrate all this.

#include <Rcpp.h>
using namespace Rcpp ;

struct A{
    int flag ;
};

A* initA(){return new A() ; }
void freeA(A* a){ delete a; }

#define GET_SET(_type_,_name_)                  \
_type_ get_##_name_(){ return obj->_name_ ; }   \
void set_##_name_(_type_ x){ obj->_name_ = x ; }

RCPP_EXPOSED_CLASS(A_cpp);
class A_cpp {
public:
    A_cpp( ) : obj( initA() ){}
    ~A_cpp(){ freeA(obj); obj = NULL ; }

    GET_SET(int,flag)

private:
    A* obj ;
} ;

int get_flag( SEXP s4obj ){
    A_cpp& obj = as<A_cpp&>( s4obj ) ;
    return obj.get_flag() ;
}

RCPP_MODULE(Whatever){
    class_<A_cpp>( "A" )
        .constructor()
        .property( "flag", &A_cpp::get_flag, &A_cpp::set_flag )
    ;
    function( "get_flag", &get_flag ) ;

}


/*** R

a <- new( A )
a$flag
a$flag <- 12L
a$flag
get_flag( a )

***/

I hope this clarifies a few things, or at least puts you on a good track.

Let us know.

Romain



Thanks.

Wush

Links:
------
[1] http://rpubs.com/wush978/rhiredis
_______________________________________________
Rcpp-devel mailing list
[email protected]
https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel

Reply via email to