在Rcpp中,如何将用户定义的结构从C获取到R

问题描述 投票:0回答:1

我正在使用 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 中甚至会有什么形式:命名列表?

谢谢!

rcpp
1个回答
12
投票

R 中的正确结构取决于您的

struct
到底是什么样子。命名列表是最通用的列表。这里有一个
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
© www.soinside.com 2019 - 2024. All rights reserved.