[Rd] Heap access across multiple calls from R to C++

2023-05-23 Thread pikappa.devel
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]]

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel


Re: [Rd] Heap access across multiple calls from R to C++

2023-05-27 Thread pikappa.devel


Thanks a lot! Also, for suggesting using `.Call`. I will try all three and
follow up on the relevant list if I hit a dead-end.

Best wishes,
Pantelis

-Original Message-
From: Dirk Eddelbuettel  
Sent: Wednesday, May 24, 2023 4:16 PM
To: Ivan Krylov 
Cc: pikappa.de...@gmail.com; r-devel@r-project.org
Subject: Re: [Rd] Heap access across multiple calls from R to C++


On 24 May 2023 at 09:09, Ivan Krylov wrote:
| On Wed, 24 May 2023 02:08:25 +0200
|  wrote:
| > 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-a
| nd-weak-references

Yes. We had numerous threads here and on r-package-devel (better list for
the question IMHO, maybe follow-up there?) stating that new code really
should avoid .C() and stick with .Call().

| 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.

(And Rcpp::XPtr sets those for you if you define RCPP_USE_FINALIZE_ON_EXIT.)

A simpler approach is of course available too, and I used it too.  Define
your class as you would in C++.  Then define
 - a helper function to initialize a class object via `new`, assign it to a
   static variable or a global pointer
 - helper functions for setters and getters of the values you need
 - a teardown function you can call, you may even tie it to R's on.exit()

Now _you_ control lifetime and allocation. From R. If anything breaks, you
get to keep the pieces.

We can also help on the rcpp-devel list.

Cheers, Dirk

--
dirk.eddelbuettel.com | @eddelbuettel | e...@debian.org

__
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel