I am about to write an extension package for R in C++ and wonder how dynamic memory management is intended to be used without risk of memory leaks. I have read
- http://cran.r-project.org/doc/manuals/R-exts.html#Memory-allocation
- http://cran.r-project.org/doc/manuals/R-exts.html#Garbage-Collection
and immediately get to three questions:
Does R gracefully unwind the C++ stack frame in case of R-exceptions, e.g. when
R_allocruns out of memory orRf_erroris called due to some other condition? – Otherwise, how am I supposed to clean up alreadyR_alloc'ed andPROTECTed or simplyCalloc'ed memory? For example, will#include<R.h> // […] void someMethod () { char *buffer1 = NULL; char *buffer2 = NULL; try { ClassA a; buffer1 = R_Calloc( 10000, char ); buffer2 = R_Calloc( 10000, char ); // […] } finally { try { if ( NULL != buffer1 ) { R_Free( buffer1 ); } } finally { if ( NULL != buffer2 ) { R_Free( buffer2 ); } } } }guarantee to call the destructor
~ClassAforaandR_Freeforbuffer1andbuffer2? And if not, what would be the R textbook way to guarantee that?- Could standard C++ (nowadays deprecated)
std::auto_ptror modernstd::unique_ptrbe employed to simplify the memory allocation idiom? - Is there a proven C++ idiom/best practice to use R's memory allocation in the C++ standard template library, e.g. some suitable allocator template, so that STL classes allocate their memory from the R heap?
Since Rf_error will indeed skip the C++ stack frame and thus bypass destructor calls, I found it necessary to undertake more documentation research. In particular a look into the RODBC package and experimentation monitoring memory use to confirm the findings, made me arrive at:
1: Immediately store pointer in an R external pointer and register a finaliser for that.
The idiom is illustrated in the following somewhat simplistic example:
The assignment of
NULLto the pointer inR_ClearExternalPtr( handle );prevents double calling of R_Free( pointer );`.Mind that there is still some assumption needed for the suggested idiom to safely work: If the constructor must not fail in the sense of R, i.e. by calling
Rf_error. If this cannot be avoided, my advice would be to postpone the constructor invocation to after the finaliser registration so that the finaliser will in any case be able toR_Freethe memory. However, logic must be included in order not to call the destructor~Aunless theAobject has been validly constructed. In easy cases, e.g. whenAcomprises only primitive fields, this may not be an issue, but in more complicated cases, I suggest to wrapAinto astructwhich can then remember whether theAconstructor completed successfully, and then allocate memory for that struct. Of course, we must still rely on theAconstructor to gracefully fail, freeing all memory it had allocated, regardless of whether this was done byC_allocormallocor the like. (Experimentation showed that memory fromR_allocis automatically freed in case ofRf_error.)2: No.
Neither class has anything to do with registering R external pointer finalisers.
3: Yes.
As far as I have seen, it is considered best practice to cleanly separate the reigns of C++ and R. Rcpp encourages the use of wrappers (https://stat.ethz.ch/pipermail/r-devel/2010-May/057387.html,
cxxfunctionin http://dirk.eddelbuettel.com/code/rcpp.html) so that C++ exceptions will not hit the R engine.In my opinion, an allocator could be programmed to use
R_CallocandR_Free. However, to counter the effects of potentialRf_errorduring such calls, the allocator would require some interface to garbage collection. I imagine locally tying the allocator to aPROTECTedSEXPof typeexternalptrwhich has a finaliser registered byR_RegisterCFinalizerExand points to a local memory manager which can free memory in case ofRf_error.