[Rcpp-devel] What is the best practice to expose a C structure from 3rd party library into R?
romain at r-enthusiasts.com
romain at r-enthusiasts.com
Mon Jul 22 13:19:54 CEST 2013
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
More information about the Rcpp-devel
mailing list