从 R 包中的 Rcpp 返回用户定义的结构

Returning a user-defined structure from Rcpp in an R package

Rcpp 功能强大,在大多数情况下都运行良好,但我不知道如何将 returns 用户定义结构的 C 函数包装到 R 包中。

描述

Package: myPackage
Type: Package
Title: What the Package Does (Title Case)
Version: 0.1.0
Depends: R (>= 4.0.0)
License: GPL-3
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.1.0
Imports:
    Rcpp
Suggests: 
    knitr,
    rmarkdown,
    testthat
VignetteBuilder: knitr
LinkingTo: 
    Rcpp

命名空间

# Generated by roxygen2: do not edit by hand

importFrom(Rcpp,evalCpp)
export(read_header)
useDynLib(myPackage, .registration = TRUE)

头文件("inst/include/myPackage_types.h")

#include <Rcpp.h>

namespace Rcpp {

  typedef struct {
    int my_data;
  } MY_HEADER_INFO;

  template <> SEXP wrap(const MY_HEADER_INFO& x) {
    std::vector<std::string> names;
    std::vector<SEXP> elements(1);
    // do something with the elements and names
    names.push_back("my_data");
    elements[0] = Rcpp::wrap( x.my_data );
    return 0;
  };

}

C 代码

/*
        read_header.c
*/

#include <stdio.h>
#include <stdlib.h>

#include <Rcpp.h>
#include "inst/include/myPackage_types.h"

namespace Rcpp {

// [[Rcpp::export]]
  Rcpp::MY_HEADER_INFO read_header() {
    Rcpp::MY_HEADER_INFO *header;
    
    header = (Rcpp::MY_HEADER_INFO*)malloc(sizeof(Rcpp::MY_HEADER_INFO));
    memset(header, 0, sizeof(Rcpp::MY_HEADER_INFO));
    
    return *header;
  }

}

当我获取(即编译)C 代码时,我得到以下信息:

Error in dyn.load("/private/var/folders/gl/jvj9b0xn34lgq6_h9370p8q80000gn/T/RtmpYRaBGW/sourceCpp-x86_64-apple-darwin17.0-1.0.4.6/sourcecpp_25a13fe3d7da/sourceCpp_49.so") : 
  unable to load shared object ...

当我尝试构建包时 (CMD + Shift + B),我得到:

clang++ -mmacosx-version-min=10.13 -std=gnu++11 -I"/Library/Frameworks/R.framework/Resources/include" -DNDEBUG  -I'/Library/Frameworks/R.framework/Versions/4.0/Resources/library/Rcpp/include' -I/usr/local/include   -fPIC  -Wall -g -O2  -UNDEBUG -Wall -pedantic -g -O0 -fdiagnostics-color=always -c RcppExports.cpp -o RcppExports.o
   RcppExports.cpp:9:7: error: no type named 'MY_HEADER_INFO' in namespace 'Rcpp'
   Rcpp::MY_HEADER_INFO read_header();
   ~~~~~~^

这看起来很简单,但我找不到类似的小插图或示例,而且我尝试过的 none 变体似乎有效。 SourceCpp动态加载报错是什么原因?为什么编译器找不到头文件中的代码?

谢谢!

我让这个工作并post将我的解决方案修改为GitHub:git@github.com:markrbower/myPackage.git

关键部分是: inst/include/myPackage_types.h

#include <RcppCommon.h>

namespace Rcpp {
  typedef struct {
    int my_data;
  } MY_HEADER_INFO;

  template <> SEXP wrap(const MY_HEADER_INFO& x);
  
  template<> MY_HEADER_INFO* as(SEXP x);
} 

read_header.cpp

/*
        read_header.c
*/

#include <stdio.h>
#include <stdlib.h>

#include "../inst/include/myPackage_types.h"

#include <RcppCommon.h>


namespace Rcpp {

  template <> SEXP wrap(const MY_HEADER_INFO& x);

}

#include <Rcpp.h>

namespace Rcpp {
  template <> SEXP wrap(const MY_HEADER_INFO& x) {
    std::vector<std::string> names;
    std::vector<SEXP> elements(1);
    // do something with the elements and names
    names.push_back("my_data");
    elements[0] = wrap( x.my_data );
    
    Rcpp::List result(elements.size());
    for (size_t i = 0; i < elements.size(); ++i) {
      result[i] = elements[i];
    }
    result.attr("names") = Rcpp::wrap(names);
    // result can be return to R as a list   
    return( result );
  };
}

//' @importFrom Rcpp evalCpp
//' @useDynLib myPackage
//' @export
// [[Rcpp::export]]
  Rcpp::MY_HEADER_INFO read_header() {
    Rcpp::MY_HEADER_INFO *header = NULL;
    
    printf( "%ld\n", sizeof(Rcpp::MY_HEADER_INFO) );
    
    header = (Rcpp::MY_HEADER_INFO*)malloc(sizeof(Rcpp::MY_HEADER_INFO));
    memset(header, 0, sizeof(Rcpp::MY_HEADER_INFO));
    
    header->my_data = 10;
    
    return *header;
}

有两个问题。首先,我在错误的 Rcpp header 下有模板定义(将初始调用放在 RcppCommon.h 之后,将详细调用放在 Rcpp.h 之后)。教程和示例警告我不要那样做,但我还是照做了。其次,我发现如果您“获取”代码然后然后“加载”库,则获取的代码会掩盖库代码,您将得到一个“空指针”错误。 运行“devtools::check()”向我展示了这一点,同时指出修复是“rm”源函数。

我还要补充一点,我需要将两个 Roxygen 注释添加到我的 .cpp 文件中,以使适当的命令出现在我的 NAMESPACE 文件中: //' @importFrom Rcpp evalCpp //' @useDynLib myPackage

我找到了一个旧的 post,其中 Dirk 建议使用 Rcpp.package.skeleton 函数构建一个“婴儿”项目,然后慢慢添加东西,直到您可以做您想做的事。这 so 比我一直使用的方法要好得多:从一个复杂的 C 程序开始,然后尝试将我的代码硬塞进 Rcpp。