简体   繁体   English

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

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

Rcpp is powerful and has worked great in most all cases, but I cannot figure out how to wrap a C-function that returns a user-defined structure into an R package. Rcpp 功能强大,在大多数情况下都运行良好,但我不知道如何将返回用户定义结构的 C 函数包装到 R package 中。

DESCRIPTION描述

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

NAMESPACE命名空间

# Generated by roxygen2: do not edit by hand

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

Header File ("inst/include/myPackage_types.h") Header 文件(“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-code 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;
  }

}

When I source (ie, compile) the C-code, I get the following:当我获取(即编译)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 ...

When I try to build the package (CMD + Shift + B), I get:当我尝试构建 package (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();
   ~~~~~~^

This seems so simple, but I can't find a similar vignetter or example and none of the variations I've tried seem to work.这看起来很简单,但我找不到类似的插图或示例,而且我尝试过的所有变体似乎都不起作用。 What is the cause of the dynamical loading error for SourceCpp? SourceCpp 动态加载错误的原因是什么? Why can't the compiler find the code in the header file?为什么编译器在 header 文件中找不到代码?

Thanks!谢谢!

I got this to work and have posted my solution to GitHub: git@github.com:markrbower/myPackage.git我得到了这个工作并将我的解决方案发布到 GitHub:git@github.com:markrbower/myPackage.git

The key parts are: inst/include/myPackage_types.h关键部分是: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.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;
}

There were two problems.有两个问题。 First, I had template definitions under the wrong Rcpp header (put the initial calls after RcppCommon.h and the detailed calls after Rcpp.h).首先,我在错误的 Rcpp header 下有模板定义(将初始调用放在 RcppCommon.h 之后,将详细调用放在 Rcpp.h 之后)。 The tutorials and examples warned me not to do that, but I did it, anyway.教程和示例警告我不要这样做,但无论如何我还是做到了。 Second, I found that if you "source" the code and then "load" the library, the sourced code will obscure the library code and you will get a "null pointer" error.其次,我发现如果你“源”代码然后“加载”库,源代码会掩盖库代码,你会得到一个“空指针”错误。 Running "devtools::check()" showed me that along with noting that the fix is to "rm" the sourced function.运行“devtools::check()”向我展示了该修复是“rm”来源的 function。

Let me also add that there are two Roxygen comments that I needed to add to my.cpp file to get the appropriate commands to appear in my NAMESPACE file: //' @importFrom Rcpp evalCpp //' @useDynLib myPackage我还要补充一点,我需要将两个 Roxygen 注释添加到 my.cpp 文件中,以使适当的命令出现在我的 NAMESPACE 文件中: //' @importFrom Rcpp evalCpp //' @useDynLib myPackage

I found an old post where Dirk suggested using the Rcpp.package.skeleton function to build a "baby" project and then slowly add things until you can do what you want.我发现了一个旧帖子,Dirk 建议使用 Rcpp.package.skeleton function 来构建一个“婴儿”项目,然后慢慢添加东西,直到你可以做你想做的事情。 That is so much better than the approach I have been using: Start with a complex C program and try to shoehorn my code into Rcpp.这比我一直使用的方法好得多:从一个复杂的 C 程序开始,然后尝试将我的代码硬塞到 Rcpp 中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM