在R中存储C对象

时间:2011-08-11 20:48:13

标签: c r

在R Extensions手册中,我找到了有关从C访问R对象的信息。但是,在我的情况下,我正在使用其他人的C代码,它具有专门的数据结构(称之为Foo)。我的目标是让R函数具有:

  • 初始化一个Foo对象。我不想在R的列表或矩阵中存储其中一个。
  • 更新Foo对象。我不想重新创建Foo对象来执行此操作,而是在适当的位置修改它。此外,此函数可能会返回有关更新是否成功的信息。
  • 从内存中删除Foo对象。

换句话说,我想在R环境中保留一个C对象,使用R函数(由C函数支持)来创建,修改和删除它。

提前感谢任何建议。

1 个答案:

答案 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);
}