如何构造一个命名列表(一个 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>
由于您要求的是普通方式,因此您需要使用 mkChar
和 SET_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;
我通过 .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>
由于您要求的是普通方式,因此您需要使用 mkChar
和 SET_STRING_ELT
:
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;