《编写R扩展》的第5.13,External pointers and weak references节规定:
外部指针SEXP旨在处理对C语言的引用 struct ,并在包中用于此目的 例如,RODBC.它们在复制语义方面是不寻常的,因为 复制R对象时,不会复制外部指针对象 复制的.(因此,外部指针应仅用作 具有正常语义的对象的一部分,例如属性或 列表中的元素.)
What is meant here by "external pointer object", the external pointer itself or the memory that the external pointer points to? Why would the unusual copying semantics mean that external pointers should only be used as part of an object with normal semantics?个
澄清一下,我的R包是一个C库Baz的包装器.Baz库提供了一个C struct Foo
,Baz将其用作一种内部工作空间.Baz提供C函数Foo* baz_allocate_foo()
和void baz_free_foo(Foo*)
来分配和释放Foo
个 struct ,这是在R内存管理之外完成的.
在我的R包中,我想使用外部指针来存储这Foo
个已分配 struct 的地址.我的R包的部分C++代码(使用RCPP进行接口)如下所示:
// baz.h is the Baz C library header; contains the definition of struct Foo
#include <R.h>
#include <Rinternals.h>
extern "C" {
#include <baz.h>
}
// For use as the external pointer's tag
#define FOO_CODE 0xF00C0DE
// Finalizer for garbage collection of external pointers to Foo
void finalize_foo(SEXP x)
{
baz_free_foo(reinterpret_cast<Foo*>(R_ExternalPtrAddr(x)));
R_ClearExternalPtr(x);
}
// Exported function for users of my R package
// [[Rcpp::export]]
SEXP get_foo()
{
SEXP tag = PROTECT(Rf_ScalarInteger(FOO_CODE));
SEXP x = PROTECT(R_MakeExternalPtr(baz_allocate_foo(), tag, R_NilValue));
R_RegisterCFinalizerEx(x, finalize_foo, TRUE);
UNPROTECT(2);
return x;
}
在R代码中,用户会这样做:
myfoo = bazwrap::get_foo()
bazwrap::say_hello(myfoo, "Alice")
bazwrap::say_goodbye(myfoo, "Bob")
其目的是在对myfoo
进行垃圾回收时或在R退出之前释放myfoo
指向的内存.
因此,我在这里使用的是一个完全独立的外部指针,而不是作为列表的一部分,也不是像编写R扩展所建议的那样,作为具有"正常语义"的对象的属性.我没有发现这有任何问题,即使在做例如
library(bazwrap)
myfoo1 = get_foo()
myfoo2 = myfoo1
rm(myfoo2)
gc() # as expected, this does not trigger the finalizer as myfoo1 is still around
say_hello(myfoo1, "Alice") # doesn't crash...
这就引出了我在帖子顶部用粗体提出的问题.