Хранение объектов C в R - PullRequest
16 голосов
/ 12 августа 2011

В руководстве по R-расширениям я нашел информацию о доступе к R-объектам из C. Однако в моей ситуации я работаю с чужим C-кодом, который имеет специализированную структуру данных (назовем это Foo).Моя цель - получить функции R, которые:

  • инициализируют объект Foo.Я не хочу хранить один из них в терминах списков или матриц R.
  • обновить объект Foo.Я не хочу пересоздавать объект Foo, чтобы сделать это, а изменить его на месте.Кроме того, эта функция может возвращать информацию о том, было ли обновление успешным.
  • удалить объект Foo из памяти.

Другими словами, я хотел бы сохранить объект C всреда R, использующая функции R (поддерживаемые функциями C) для ее создания, изменения и удаления.

Заранее благодарим за любые советы.

1 Ответ

28 голосов
/ 12 августа 2011

Как уже упоминалось, идея заключается в использовании внешнего указателя.Это описано в руководстве 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...