Skip to content

[Rcpp-devel] class methods and R_RegisterCCallable

2 messages · Leonardo Silvestri, Serguei Sokol

#
I believe Dirk is correct in saying the traditional mechanism won't work 
for non-static member functions, but if the C++ code is fully contained 
in header files in 'inst/include', then it's possible to access that 
code from another package since there's no library that one needs to 
link against. I believe that's the mechanism that Rcpp itself uses.

Things get ugly when one tries to link to another package's library. 
I've tried a few experiments with Dirk's help 
(https://github.com/lsilvest/linktest), but haven't found a reliable way 
to make this portable (things break when packages are installed 
pre-compiled).

Leo
3 days later
#
Le 24/10/2019 à 18:23, Leonardo Silvestri a écrit :
Thanks for sharing, it saved me some iterations of trying.

I tested a direct exposing of member function with
R_RegisterCCallable("pkgM", "Num__setx", (DL_FUNC) &Num::setX);

and then passing a call with a first argument as pointer to an object.
It worked on my Linux with gcc 8.3 with a warning at
(DL_FUNC) &Num::setX. Then I read here 
https://isocpp.org/wiki/faq/pointers-to-members that such kind of cast 
are explicetly prohibeted as inducing undefined behavior. So my working 
example was just a chance.

As Dirk suggested, wrapping a method in a plain C-line function worked.
Here, for the record, main steps:
0. put Num class definition in pkgM/inst/include/pkgM.h
1. in pkgM/Num.cpp

void wrap_Num__setx(Num *pobj, double val) {
     pobj->setX(val);
}

2. in pkgM/RcppExports.cpp add
     R_RegisterCCallable("pkgM", "Num__setx", (DL_FUNC) &wrap_Num__setx);
to R_init_pkgM(DllInfo *dll) { ...

3. in pkgA/DESCRIPTION
LinkingTo: Rcpp, pkgM
Depends: pkgM

4. in pkgA/rcpp_hello_world.cpp
// [[Rcpp::export]]
void a_setx(XPtr<Num> xp, double val)
{
   XPtr<Num> p(xp);
   static void (*fun)(Num*, double) = NULL;
   if (fun == NULL) fun = (void (*)(Num*, double)) 
R_GetCCallable("pkgM","Num__setx");
   fun(p, val);
}

5. in R session
library(pkgA) # loads pkgM too
num=Num$new() # created with pkgM infrastructure
a_setx(num at .xData$.pointer, 100)
num$x
#[1] 100

Best,
Serguei.