【问题标题】:Returning a user-defined structure from Rcpp in an R package从 R 包中的 Rcpp 返回用户定义的结构
【发布时间】:2020-10-26 00:49:39
【问题描述】:

Rcpp 功能强大,在大多数情况下都运行良好,但我不知道如何将返回用户定义结构的 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();
   ~~~~~~^

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

谢谢!

【问题讨论】:

  • 这不是你昨天问的同一个问题(stackoverflow.com/q/62734032/2554330)吗?有什么新鲜事?
  • 我不确定你有什么,但从错误来看,你似乎没有定义MY_HEADER_INFO。您定义它的方式在C 中有效。在C++ 中,您应该在标识符struct 之后紧跟结构名称。你试过那个吗?
  • user2554330,是的,它很相似,但 Dirk 很好地解释了 Rcpp 如何查找包含文件(我的第一个问题),这帮助我解决了一个错误消息。唉,另一个紧随其后。 StackOverflow“神”对问题漂移皱眉,所以我认为最好在 Dirk 对第一个问题的回答之后开始一个新问题。 Onyambu,我在头文件中尝试了“typedef struct MY_HEADER_INFO”,但错误是一样的。不过,我喜欢你的评论,因为我觉得我被困在一些相当微不足道的事情上(好吧,我_希望_它微不足道!),最轻微的改变会让一切正常。
  • 不,这不是“微不足道的”,因为我们就该主题写了完整的pdf vignette。而且我猜你没有通过重复太多之前(现在已解决)的问题来帮助你的案子“我如何让struct 定义已知”阻碍了关于“如何添加代码来映射我的@ 987654334@ 到 R 对象”。而且没有捷径或灵丹妙药。

标签: r package rcpp


【解决方案1】:

我得到了这个工作并将我的解决方案发布到 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 标头下有模板定义(将初始调用放在 RcppCommon.h 之后,将详细调用放在 Rcpp.h 之后)。教程和示例警告我不要这样做,但无论如何我还是做到了。其次,我发现如果你“源”代码并然后“加载”库,源代码将掩盖库代码,你会得到一个“空指针”错误。运行“devtools::check()”向我展示了这一点,同时注意到修复是“rm”源函数。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-09
    • 2020-03-17
    • 2017-03-11
    • 2020-10-25
    • 2020-05-08
    • 2022-01-12
    • 1970-01-01
    相关资源
    最近更新 更多