R .call() interface and EXTPTRSXP: Understanding PROTECT/UNPROTECT with externally allocated objects
Asked Answered
C

2

6

In the following code, object of type foo is created with a call to foo_new() and an external-pointer to the object is returned to R. Subsequent computations are performed by passing ptr_foo. The object is eventually freed with an explicit call to foo_free(foo *X). All computations are performed by libfoo.

Does the fact that ptr_foo was created mean that all other dynamically allocated fields within the foo object are automatically protected? Or, is it possible that fields such as "bar" may be swept away by the garbage collector?

SEXP foo_new (SEXP n) {
    SEXP ptr_foo;
    foo *X = (foo*) foo_new( 1, sizeof(foo) );
    //foo is protected from garbage collection
    assert( X );
    X->bar = (int*) foo_add_bar(INTEGER_VALUE(n));
    //Is bar protected from garbage collection?
    assert(X->bar);
    PROTECT( ptr_foo = R_MakeExternalPtr(X, install("extptr_foo"), R_NilValue) );
    R_RegisterCFinalizerEx( ptr_foo, ptr_foo_finalize, 1 );
    UNPROTECT( 1 );
    return (ptr_foo);
} 

Thanks,

RT

Counterwork answered 20/7, 2011 at 4:41 Comment(0)
A
3

It looks like your foo object is your own creation (not and SEXP). If so, it has nothing to do with R and is NOT garbage collected and therefore does not need to be/can't be protected. No one will look at it or its fields but you.

The bar object you put in it is also your own creation and not an R object (an SEXP) I assume. If it IS an SEXP or points to data within an SEXP then that data needs to be protected. A safer/easier way would then be to make a copy of the data in the SEXP.

When the ptr_foo object is no longer used by R and garbage collected, your ptr_foo_finalize function will be called to remove your foo object (and the bar part).

Aquinas answered 21/7, 2011 at 0:22 Comment(1)
Thanks Tommy. That is what I was hoping for. As you indicate, all exchange of data between R and libfoo happens by copying SEXPs in and out. All objects allocated by the library are freed by a foo_free() call.Counterwork
A
1

For starters, you are not supposed to use calloc() or malloc() for R objects, the "Writing R Extensions" manual is pretty clear on that.

Second, each allocation would get its own PROTECT all.

Third, external pointer objects are R representations of something created elsewhere (for a canonical example , see the RODBC package and its implementation of DB interface). I don't think you're supposed to create external pointer objects from within.

Armistead answered 20/7, 2011 at 11:49 Comment(1)
Thanks Dirk, I edited my original code. I used calloc() just to keep the example simple. Creation, destruction and computations involving foo and bar are performed by calls to functions in libfoo. Thanks for the pointer to RODBC package.Counterwork

© 2022 - 2024 — McMap. All rights reserved.