pik@pp@@devei m@iii@g oii gm@ii@com
2023-May-24 00:08 UTC
[Rd] Heap access across multiple calls from R to C++
Dear all, I have a question about maintaining state in native code during the lifetime of an object of R6Class type. The background is the following. I have a class derived from the standard model class of the R port of Keras, and I want to perform some calculations in native code using a third-party library. I am working with R's C interface. To avoid memory initializations and some indexing calculations during the model's training, I want to initialize a native structure on the heap during the construction of the R6Class, prepare everything, and use this structure later to perform the calculations on the native side. As you can see, this requires maintaining the state during successive calls to my native code, and because of the third-party software I am relying on, I do not see an efficient way to organize my code differently. I think I have a working way of doing what I intend, but I am skeptical about its portability, and I would like to get some feedback or ideas for a better implementation. So, a minimalistic description of my approach goes along the following line (not tested example snippet): In R: R6::R6Class("my_model", public = list( native_obj = double(1), initialize = function() { .C("make_native_obj", self$native_obj) }), private = list ( finalize = function() { .C("release_native_obj", self$native_obj) })) In C(++): extern "C" void make_native_obj(double *obj) { auto native_obj = new NativeObjStruct(); memcpy(obj, &native_obj, sizeof(obj)); } extern "C" void release_native_obj(double *obj) { NativeObjStruct *native_obj{nullptr}; memcpy(&native_obj, obj, sizeof(native_obj)); delete native_obj ; } This assumes that sizeof(double)=8=sizeof(native_obj*) and works on my machine with g++, clang, and msvc. Although I have never personally used a setup in which this condition is false, I wonder if it is universal in all the platforms relevant to R. Is there a better way to do this? From the C++ side, uintptr_t would be cleaner, but if I am not mistaken, I would then need integer(2) on the R side. Would this be better? Is there maybe a more standard way that this problem is solved in other cases? I have searched the R internals page, but I could not find something to help me. Essentially, I am asking for an approach corresponding to PyCapsule of the Python C API. I am looking for alternatives, preferably as lightweight as possible and, at least portable to the CRAN platforms. I would be very thankful for any suggestions or tips! Kind Regards, Pantelis [[alternative HTML version deleted]]
On Wed, 24 May 2023 02:08:25 +0200 <pikappa.devel at gmail.com> wrote:> initialize = function() { > > .C("make_native_obj", self$native_obj) > > })> extern "C" void make_native_obj(double *obj) { > > auto native_obj = new NativeObjStruct(); > > memcpy(obj, &native_obj, sizeof(obj)); > > }> Is there a better way to do this?The .Call() interface (where functions take an arbitrary number of native R objects and return a native R object) combined with external pointers is likely to be a better approach here: https://cran.r-project.org/doc/manuals/R-exts.html#External-pointers-and-weak-references On the R side, the returned SEXP will have class 'externalptr', with not much to do about it programmatically. C code can additionally register a finalizer on the object in order to release the memory automatically after the it is discarded. -- Best regards, Ivan