在R Extensions手册中,我找到了有关从C访问R对象的信息。但是,在我的情况下,我正在使用其他人的C代码,它具有专门的数据结构(称之为Foo)。我的目标是让R函数具有:
换句话说,我想在R环境中保留一个C对象,使用R函数(由C函数支持)来创建,修改和删除它。
提前感谢任何建议。
答案 0 :(得分:28)
如上所述,我们的想法是使用外部指针。 “写入R扩展”手册中对此进行了描述。这是一个简单的例子。
包含相关的R标题
#include <Rdefines.h>
我们将创建一个可以容纳长度最多为15的C char *的对象
const int N_MAX=15;
当外部指针不再由任何R对象表示时,外部指针就像一个终结器。我们在这里安全地玩它,在释放之前检查指针地址是否有效(使用Free,因为我们将使用Calloc进行分配 - 这些是C级内存分配函数,这些函数在调用C时持续存在,与R_alloc不同)并清除指针表明它已经最终确定。
static void
_finalizer(SEXP ext)
{
if (NULL == R_ExternalPtrAddr(ext))
return;
Rprintf("finalizing\n");
char *ptr = (char *) R_ExternalPtrAddr(ext);
Free(ptr);
R_ClearExternalPtr(ext);
}
这是我们的构造函数。它需要一些R'信息'它随身携带(在本例中稍后不再使用)然后为短字符串分配一些内存。我们用x和info构造一个外部指针(R_NilValue是一个'tag',按照惯例我们可以用来标记我们的对象 - mkString(“MyCObject”)或类似的)。我们将终结器与外部指针相关联。 PROTECT / UNPROTECT用于防止通过调用R_RegisterCFinalizerEx触发垃圾收集器。当R分配内存时,可以调用垃圾收集器;很难知道这种情况何时发生(我们可以跟踪代码流),所以我们安全地播放它并在创建它时将外部指针添加到PROTECT。
SEXP
create(SEXP info)
{
char *x = Calloc(N_MAX, char);
snprintf(x, N_MAX, "my name is joe");
SEXP ext = PROTECT(R_MakeExternalPtr(x, R_NilValue, info));
R_RegisterCFinalizerEx(ext, _finalizer, TRUE);
UNPROTECT(1);
return ext;
}
这是getter,只是引用外部指针地址并将其作为R字符返回(1)
SEXP
get(SEXP ext)
{
return mkString((char *) R_ExternalPtrAddr(ext));
}
和一个带有外部指针和字符(1)的setter,将str的第一个元素的C表示复制到我们的对象中。我们返回逻辑(1),但可以返回任何内容。
SEXP
set(SEXP ext, SEXP str)
{
char *x = (char *) R_ExternalPtrAddr(ext);
snprintf(x, N_MAX, CHAR(STRING_ELT(str, 0)));
return ScalarLogical(TRUE);
}
如果这是在文件tmp.c中,我们用
编译R CMD SHLIB tmp.c
或将其作为文件src/tmp.c
集成到一个包中,并正常构建包。使用:
> dyn.load("tmp.so")
> x <- .Call("create", list("info could be any R object", 1:5))
> .Call("get", x)
[1] "my name is joe"
> ## reference semantics!
> .Call("set", x, "i am sam i am")
[1] TRUE
> .Call("get", x)
[1] "i am sam i am"
> x <- NULL
> gc()
finalizing
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 339306 18.2 467875 25 407500 21.8
Vcells 202064 1.6 786432 6 380515 3.0
这是第二个示例,其中包含一个增加的int的结构。
#include <Rdefines.h>
struct Foo {
int x;
};
static void
_finalizer(SEXP ext)
{
struct Foo *ptr = (struct Foo*) R_ExternalPtrAddr(ext);
Free(ptr);
}
SEXP
create()
{
struct Foo *foo = Calloc(1, struct Foo);
foo->x = 0;
SEXP ext = PROTECT(R_MakeExternalPtr(foo, R_NilValue, R_NilValue));
R_RegisterCFinalizerEx(ext, _finalizer, TRUE);
UNPROTECT(1);
return ext;
}
SEXP
incr(SEXP ext)
{
struct Foo *foo = (struct Foo*) R_ExternalPtrAddr(ext);
foo->x += 1;
return ScalarInteger(foo->x);
}