【问题标题】:In Rcpp, how to get a user-defined structure from C into R在 Rcpp 中,如何将用户定义的结构从 C 获取到 R
【发布时间】:2018-06-29 22:45:31
【问题描述】:

我正在使用 Rcpp 包,并且可以让我的 C 函数在 R 中编译和运行,但现在我想向 R 返回一个用户定义的大型数据结构。结构中的字段是数字或字符串 - 没有新的或结构中的奇数类型。下面的示例已简化并且无法编译,但它传达了我的问题的想法。

    typedef struct {
        char*   firstname[128];
        char*   lastname[128];
        int      nbrOfSamples;
    } HEADER_INFO;

    // [[Rcpp::export]]
    HEADER_INFO* read_header(Rcpp::StringVector strings) {
        FILE *fp;
        MEF_HEADER_INFO *header;

        char * filename = (char*)(strings(0));
        char * password = (char*)(strings(1));

        header = (HEADER_INFO*)malloc(sizeof(HEADER_INFO));
        memset(header, 0, sizeof(HEADER_INFO));

        fp = fopen(filename, "r");
        (void)read_header(header, password);
        return header;
    }

我很确定我可以将标头中的条目打包回 StringVector,但这似乎是一种蛮力方法。我的问题是是否存在更优雅的解决方案。我不清楚这种结构在 R 中会有什么形式:命名列表?

谢谢!

【问题讨论】:

  • c.f.在 Rcpp 库@gallery.rcpp.org/articles/…gallery.rcpp.org/articles/… 上的 Rcpp 中自定义模板化和包装函数
  • BTW 它被称为 'Rcpp' 所以 cpp 是小写的。它有文档,包括专门针对这个(不是微不足道但很重要)主题的整个小插曲。总之,你需要写转换器。
  • 偶然我今天早上看到您发布了一个附加问题作为“答案”,现在已经消失了。如果它被删除为“不是答案”:发布新问题或edit您的问题以添加“奖励”材料。顺便说一句,您发布的示例由于各种原因无法编译。请尝试制作无需修改即可运行的示例。这不仅可以帮助我们帮助您,还可以帮助您隔离问题。尝试构建 MWE 时,通常可以解决问题。

标签: rcpp


【解决方案1】:

R 中的正确结构取决于您的struct 的外观。命名列表是最通用的列表。这里是 cmets 中提到的 wrap 函数的简单示例实现:

#include <RcppCommon.h>

typedef struct {
  char*   firstname[128];
  char*   lastname[128];
  int      nbrOfSamples;
} HEADER_INFO;

namespace Rcpp {
  template <>
  SEXP wrap(const HEADER_INFO& x);
}

#include <Rcpp.h>

namespace Rcpp {
  template <>
  SEXP wrap(const HEADER_INFO& x) {
    Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
    Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
    return Rcpp::wrap(Rcpp::List::create(Rcpp::Named("firstname") = firstname,
                      Rcpp::Named("lastname") = lastname,
                      Rcpp::Named("nbrOfSamples") = Rcpp::wrap(x.nbrOfSamples)));
  };
}

//  [[Rcpp::export]]
HEADER_INFO getHeaderInfo() {
  HEADER_INFO header;
  header.firstname[0] = (char*)"Albert";
  header.lastname[0] = (char*)"Einstein";
  header.firstname[1] = (char*)"Niels";
  header.lastname[1] = (char*)"Bohr";
  header.firstname[2] = (char*)"Werner";
  header.lastname[2] = (char*)"Heisenberg";
  header.nbrOfSamples = 3;
  return header;
}

/*** R
getHeaderInfo()
 */

输出:

> getHeaderInfo()
$firstname
[1] "Albert" "Niels"   "Werner"

$lastname
[1] "Einstein"   "Bohr"       "Heisenberg"

$nbrOfSamples
[1] 3

但是,对于这种特殊情况,data.frame 使用起来会更自然,这可以通过将上面的 wrap 替换为:

  template <>
  SEXP wrap(const HEADER_INFO& x) {
    Rcpp::CharacterVector firstname(x.firstname, x.firstname + x.nbrOfSamples);
    Rcpp::CharacterVector lastname(x.lastname, x.lastname + x.nbrOfSamples);
    return Rcpp::wrap(Rcpp::DataFrame::create(Rcpp::Named("firstname") = firstname,
                                              Rcpp::Named("lastname") = lastname));
  };

输出:

> getHeaderInfo()
  firstname   lastname
1    Albert   Einstein
2     Niels       Bohr
3    Werner Heisenberg

【讨论】:

  • 再一次,拉尔夫的回答非常好,对其他人也有帮助!!
  • 这对我有用 sourceCpp();需要进行哪些更改才能将其放入包中?我得到:RcppExports.cpp:9:1: error: ‘HEADER_INFO’ does not name a typeRcppExports.cpp:14:34: error: ‘getHeaderInfo’ was not declared in this scope rcpp_result_gen = Rcpp::wrap(getHeaderInfo()); ... ERROR: compilation failed
  • @nsheff 好问题!查看cran.r-project.org/web/packages/Rcpp/vignettes/…,特别是“2.5. 生成代码中的类型”部分。
  • @RalfStubner 谢谢,我可以使用 *_types.h 让它工作
猜你喜欢
  • 2020-10-26
  • 2014-02-09
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-07
  • 2020-04-08
  • 1970-01-01
相关资源
最近更新 更多