Как уже упоминалось, идея заключается в использовании внешнего указателя.Это описано в руководстве Writing R Extensions.Вот краткий пример.
Включите соответствующий заголовок R
#include <Rdefines.h>
Мы создадим объект, который может содержать C char * длины не более 15
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 является «тегом», который по соглашению мы можем использовать для обозначения нашего объекта - 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;
}
Вот геттер, просто ссылающийся наадрес внешнего указателя и его возвращение в виде символа R (1)
SEXP
get(SEXP ext)
{
return mkString((char *) R_ExternalPtrAddr(ext));
}
и метода установки, который принимает внешний указатель и символ (1), копируя представление C первого элемента str в наш объект,Мы возвращаем логическое (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);
}