[Rcpp-devel] class methods and R_RegisterCCallable
Serguei Sokol
serguei.sokol at gmail.com
Mon Oct 28 11:39:26 CET 2019
Le 24/10/2019 à 18:23, Leonardo Silvestri a écrit :
>> On 24 October 2019 at 16:01, Serguei Sokol wrote:
>> | I'am trying to find a way to access class methods of one Rcpp
>> package | (say pkgM) from C++ code in another one (say pkgA).
>> | If it were simple C++ routines and not class methods, the way is
>> known | and even automatized. E.g. if I generate pkgM with
>> | | Rcpp::Rcpp.package.skeleton("pkgM", module=TRUE)
>> | Rcpp::compileAttributes("pkgM")
>> | | then in pkgM/src/RcppExports.cpp we can see
>> | | | static const R_CallMethodDef CallEntries[] = {
>> | {"_pkgM_rcpp_hello_world", (DL_FUNC) &_pkgM_rcpp_hello_world, 0},
>> | ...
>> | | As far as I understand, it registers _pkgM_rcpp_hello_world for
>> use with | R_GetCCallable from other packages. And it is a consequence
>> of preceding
>> | rcpp_hello_world() with "// [[Rcpp::export]]".
>> | If I add "// [[Rcpp::interfaces(r, cpp)]]" in
>> pkgM/src/rcpp_module.cpp | it produces
>> pkgM/inst/include/{pkgM.h,pkgM_RcppExports.h} and
>> | | static const R_CallMethodDef CallEntries[] = {
>> | ...
>> | {"_pkgM_RcppExport_registerCCallable", (DL_FUNC) |
>> &_pkgM_RcppExport_registerCCallable, 0},
>> | ...
>> | but still nothing for methods and functions from RCPP_MODULE.
>> | | So my question, what is a canonical way (if defined) to expose
>> class | methods (via RCPP_MODULE or any other mechanism) in such a way
>> that they | become callable from C++ code of another package? Am I
>> supposed to | register them manually in some way using |
>> _pkgM_RcppExport_registerCCallable()? And if so, how can I retrieve
>> them | in C++ code of another package?
>>
>>> From the top of my head I would say "No" for the simple reason that
>>> all R
>> does (and this is the same for all other foreign-function interfaces)
>> has to
>> be C based. Hence no class methods. C++ signatures are compiler
>> dependent,
>> and have been avoided for that reason for as long as we had them.
>>
>> So I fear you need plain C wrappers for every member function you want to
>> expose. I could of course be wrong -- if someone else has
>> counter-examples I
>> would be all ears!
>>
>> Dirk
>>
>> --
>> http://dirk.eddelbuettel.com | @eddelbuettel | edd at debian.org
>
> 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).
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.
More information about the Rcpp-devel
mailing list