如何构造一个命名列表(一个 SEXP)以从使用 .Call() 调用的 C 函数中返回?

How to construct a named list (a SEXP) to be returned from the C function called with .Call()?

我通过 .Call("foo", <args>) 调用 C 代码,其中 foo 调用其他 C 函数,计算结果并 returns 它。结果是一个列表 长度为 3,我想命名这个列表。为此,佛 这样做:

/* 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 的命名列表的a/the正确方法吗?

C 函数确实 return 返回到 R 但是一旦我将输出分配给 R 中的变量,我立即得到一个分段错误。可能出了什么问题?我 可以在上面的'return之前放置'调试语句'(简单printf("...\n") 资源;'他们执行得很好。 有没有方便的方法来调试从 R 调用的 C 代码?

根据@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> 

由于您要求的是普通方式,因此您需要使用 mkCharSET_STRING_ELT:

从 C 字符串创建一个 R 字符向量
for(i = 0; i < 3; i++) SET_STRING_ELT(nms, i, mkChar(names[i]));  

现在,您正在尝试将原始 C 字符串用作 R 对象,这是行不通的。

Re:debugging,可以在C代码中使用PrintValue打印出R对象

综上所述,除非您有非常具体的原因想要纯香草味,否则您应该考虑 Rcpp

BrodieG 答案的替代方法是使用 Rinlinedfuns.h 中的 mkNamed(其中包含如何使用 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;