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:
#define STRICT_R_HEADERS true
#include <string>
#include <R.h>
#include <Rinternals.h> // defines SEXP
using namespace std;
class A {
string name;
public:
A ( const char * const name ) : name( name ) { Rprintf( "Construct %s\n", name ); }
~A () { Rprintf( "Destruct %s\n", name.c_str() ); }
const char* whoami () const { return name.c_str(); }
};
extern "C" {
void finaliseAhandle ( SEXP handle ) {
A* pointer = static_cast<A*>( R_ExternalPtrAddr( handle ) );
if ( NULL != pointer ) {
pointer->~A();
R_Free( pointer );
R_ClearExternalPtr( handle );
}
}
SEXP createAhandle ( const SEXP name ) {
A* pointer = R_Calloc( 1, A );
SEXP handle = PROTECT( R_MakeExternalPtr(
pointer,
R_NilValue, // for this simple example no use of tag and prot
R_NilValue
) );
try {
new(pointer) A( CHAR( STRING_ELT( name, 0 ) ) );
R_RegisterCFinalizerEx( handle, finaliseAhandle, TRUE );
} catch (...) {
R_Free( pointer );
R_ClearExternalPtr( handle );
Rf_error( "construction of A(\"%s\") failed", CHAR( STRING_ELT( name, 0 ) ) );
}
// … more code may follow here, including calls to Rf_error.
UNPROTECT(1);
return handle;
}
SEXP nameAhandle ( const SEXP handle ) {
A* pointer = static_cast<A*>( R_ExternalPtrAddr( handle ) );
if( NULL != pointer ) {
return mkChar( pointer->whoami() );
}
return R_NilValue;
}
SEXP destroyAhandle ( const SEXP handle ) {
if( NULL != R_ExternalPtrAddr( handle ) ) {
finaliseAhandle( handle );
}
return R_NilValue;
}
}
The assignment of NULL
to the pointer in R_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 to R_Free
the memory. However, logic must be included in order not to call the destructor ~A
unless the A
object has been validly constructed. In easy cases, e.g. when A
comprises only primitive fields, this may not be an issue, but in more complicated cases, I suggest to wrap A
into a struct
which can then remember whether the A
constructor completed successfully, and then allocate memory for that struct. Of course, we must still rely on the A
constructor to gracefully fail, freeing all memory it had allocated, regardless of whether this was done by C_alloc
or malloc
or the like. (Experimentation showed that memory from R_alloc
is automatically freed in case of Rf_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
and R_Free
. However, to counter the effects of potential Rf_error
during such calls, the allocator would require some interface to garbage collection. I imagine locally tying the allocator to a PROTECT
ed SEXP
of type externalptr
which has a finaliser registered by R_RegisterCFinalizerEx
and points to a local memory manager which can free memory in case of Rf_error
.