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_alloc
runs out of memory orRf_error
is called due to some other condition? – Otherwise, how am I supposed to clean up alreadyR_alloc
'ed andPROTECT
ed 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
~ClassA
fora
andR_Free
forbuffer1
andbuffer2
? And if not, what would be the R textbook way to guarantee that?- Could standard C++ (nowadays deprecated)
std::auto_ptr
or modernstd::unique_ptr
be 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
NULL
to 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_Free
the memory. However, logic must be included in order not to call the destructor~A
unless theA
object has been validly constructed. In easy cases, e.g. whenA
comprises only primitive fields, this may not be an issue, but in more complicated cases, I suggest to wrapA
into astruct
which can then remember whether theA
constructor completed successfully, and then allocate memory for that struct. Of course, we must still rely on theA
constructor to gracefully fail, freeing all memory it had allocated, regardless of whether this was done byC_alloc
ormalloc
or the like. (Experimentation showed that memory fromR_alloc
is 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,
cxxfunction
in 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_Calloc
andR_Free
. However, to counter the effects of potentialRf_error
during such calls, the allocator would require some interface to garbage collection. I imagine locally tying the allocator to aPROTECT
edSEXP
of typeexternalptr
which has a finaliser registered byR_RegisterCFinalizerEx
and points to a local memory manager which can free memory in case ofRf_error
.