Skip to content
Prev 10793 / 21312 Next

[Bioc-devel] PROTECT errors in Bioconductor packages

Martin Morgan wrote:
I wonder if the following is a sensible idea:

int Rf_num_protected; // global variable
void Rf_start_protection() {
    Rf_num_protected=0;
    return;
}
SEXP Rf_add_protection(SEXP x) {
   ++Rf_num_protected;
    return PROTECT(x);
}
void Rf_end_protection() {
    UNPROTECT(Rf_num_protected);
    return;
}
   
The idea would be to:

1. call Rf_start_protection() at the top of the native routine
2. replace all uses of PROTECT with Rf_add_protection
3. call Rf_end_protection() just before returning to R

This would avoid having to keep track of the number of PROTECTs
performed, which may not be trivial if the routine can return at
multiple points.

It might also useful for C++ native routine creating class instances
that need to do internal PROTECTs for the lifetime of the instance. As
long as those PROTECTs are done via Rf_add_protection(), a single
Rf_end_protection() call at the bottom of the top-level routine would be
sufficient to handle them all. In contrast, putting a matching UNPROTECT
in the class destructor is not safe, as it is possible to trigger the
destructor to UNPROTECT an unrelated SEXP:

SEXP blah(SEXP x) {
    my_class* ptr=new my_class(x); // say this does an internal PROTECT
    SEXP output=PROTECT(allocVector(INTSXP, 1));
    // ... do something with output here...
    delete ptr; // if UNPROTECT is in the destructor, it UNPROTECTs
output instead
    // ... do some more stuff, possibly involving allocations ...
    UNPROTECT(1); // this actually UNPROTECTs whatever was in my_class
    return output;
}