2015-05-16 4 views
4

Я вызываю код C через .Call("foo", <args>), где foo вызывает другие функции C , вычисляет результат и возвращает его. Результатом является список длины 3, и я бы назвал этот список. С этой целью, Foo делает это:Как построить именованный список (SEXP) для возврата из функции C, вызванной с .Call()?

/* Construct result list from variables containing the results */ 
SEXP res = PROTECT(allocVector(VECSXP, 3)); /* list of length 3 */ 
SET_VECTOR_ELT(res, 0, ScalarReal(a)); /* numeric(1) */ 
SET_VECTOR_ELT(res, 1, somenumericvector); /* numeric(<some length>) */ 
SET_VECTOR_ELT(res, 2, ScalarInteger(i)); /* integer(1) */ 

/* Name components and return */ 
SEXP nms = PROTECT(allocVector(STRSXP, 3)); /* names as SEXP */ 
char *nms_ = CHAR(STRING_ELT(nms, 0)); /* pointer to names */ 
char *names[3] = {"result_numeric", "result_numeric_vector", "result_integer"}; 
for(i = 0; i < 3; i++) nms_[i] = names[i]; 
setAttrib(res, R_NamesSymbol, nms); 
UNPROTECT(1); 
return res; 

Является ли это/правильный способ построения именованного списка длины 3?

Функция C действительно возвращается к R, но как только я назначаю вывод переменной в R, я сразу же получу ошибку сегментации. Что может быть неправильным? Я может поставить «отладочный на» (простой printf("...\n") прямо перед выше «вернуть Рез,» и они выполняются в порядке Есть ли удобный способ для отладки кода C называется из R

+2

FWIW это было бы намного проще с API-интерфейсом Rcpp. – nrussell

+1

И это еще проще, если вы задали имена из R. – nicola

+0

Я знаю, но меня интересует «простой» способ. –

ответ

6

Альтернатива ответам BrodieG заключается в использовании mkNamed от Rinlinedfuns.h (в котором содержится пример использования mkNamed).

/* Construct named result list from variables containing the results */ 
const char *names[] = {"result_numeric", "result_numeric_vector", 
    "result_integer", ""};     /* note the null string */ 
SEXP res = PROTECT(mkNamed(VECSXP, names)); /* list of length 3 */ 
SET_VECTOR_ELT(res, 0, ScalarReal(a));  /* numeric(1) */ 
SET_VECTOR_ELT(res, 1, somenumericvector); /* numeric(<some length>) */ 
SET_VECTOR_ELT(res, 2, ScalarInteger(i)); /* integer(1) */ 
UNPROTECT(1); 
return res; 
+1

Удивительно, это должен быть ответ. – BrodieG

1

Per @ nrussell хороший совет.? , раствор в одном операторе (штриховые более четыре линии для удобочитаемости)

R> cppFunction('List marius(double a, NumericVector b, int c) \ 
     { return List::create(Named("resnum")=a,\ 
          Named("resvec")=b, \ 
          Named("resint")=c); }') 
R> marius(1.2, sqrt(3:5), 42L) 
$resnum 
[1] 1.2 

$resvec 
[1] 1.73205 2.00000 2.23607 

$resint 
[1] 42 

R> 
4

Поскольку вы просите простой ванили образом, вам нужно создать вектор R символов из строк с помощью mkChar и SET_STRING_ELT:

for(i = 0; i < 3; i++) SET_STRING_ELT(nms, i, mkChar(names[i])); 

Прямо сейчас вы пытаетесь использовать необработанную строку C как объект R, который не будет работать.

Re: отладка, вы можете использовать PrintValue в коде C для печати R объектов.

Все это сказало, если у вас нет особой причины для получения простой ванили, вы должны рассмотреть Rcpp.

+0

Это не совсем то, что я сказал в комментариях? – nicola

+0

@nicola, да, это похоже, хотя и более полное. Более того, из памяти при первом чтении вы предоставили ссылку, а не файл SET_STRING_ELT. Вы редактировали или мне не хватает воспоминаний? – BrodieG

+0

И, чтобы быть понятным, я не копировал вставку. Так получилось, что, используя имена переменных OPs, мы получили то же самое. – BrodieG