【问题标题】:Rcpp functions undefined when used alongside Stan与 Stan 一起使用时未定义的 Rcpp 函数
【发布时间】:2018-06-04 18:23:06
【问题描述】:

我有一个将 Rcpp 用于某些内部功能的 R 包。这些函数不会导出供用户直接访问(请参阅rcpptest 存储库中的最小可重现示例)。

我现在尝试将 Stan 代码添加到 src/ 目录,以便在安装包时进行编译(rcppstan 存储库中的可重现示例)。但是,当我修改包以使用 Stan 时,我在 R CMD CHECK 中收到以下错误:

#> ❯ checking R code for possible problems ... NOTE
#>   meanC: no visible binding for global variable ‘_rcppstan_meanC’
#>   Undefined global functions or variables:
#>     _rcppstan_meanC

事实上,当我尝试调用使用 meanC 函数的 R 函数时,我收到一条错误消息 Error in meanC(x) : object '_rcppstan_meanC' not found

据我所知,当我修改包以使用 rstan 时发生了什么变化,因此可能是原因。

  1. 仅使用Rcpp时,src/RcppExports.cpp中如下:

    static const R_CallMethodDef CallEntries[] = {
        {"_rcpptest_timesTwo", (DL_FUNC) &_rcpptest_timesTwo, 1},
        {NULL, NULL, 0}
    };
    
    RcppExport void R_init_rcpptest(DllInfo *dll) {
        R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
        R_useDynamicSymbols(dll, FALSE);
    }
    
  2. Stan 被合并时,src/RcppExports.cpp 文件中不再生成该代码。相反,这似乎是由 rstantools 包创建的 src/init.cpp 文件处理的。该文件中的相关块在这里:

    static const R_CallMethodDef CallEntries[] = {
      {NULL, NULL, 0}
    };
    
    void attribute_visible R_init_rcppstan(DllInfo *dll) {
      // next line is necessary to avoid a NOTE from R CMD check
      R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
      R_useDynamicSymbols(dll, TRUE); // necessary for .onLoad() to work
    }
    

为什么src/init.cpp 中的代码使 Rcpp 函数未定义?相反,有没有办法编辑 src/init.cpp 以便 Stan 模型能够正确编译和访问,同时仍然允许定义 Rcpp 函数?

【问题讨论】:

  • 当我们编写rstantools::rstan_package_skeleton 时,我们从未考虑过除了Stan 程序的C++ 表示之外,还有其他编译代码的情况。如果你只是删除src/init.cpp 会发生什么?它在基于 Stan 的包中没有多大用处,但它避免了来自R CMD check 的关于存在 C++ 代码但注册的警告。您可能需要将src/RcppExports.cpp 中的R_useDynamicSymbols(dll, FALSE); 更改为R_useDynamicSymbols(dll, TRUE);
  • 当我删除src/init.cpp 时,包无法构建:make: *** No rule to make target 'init.o', needed by 'rcppstan.so'. Stop. rm stan_files/uni_irt.cc ERROR: compilation failed for package ‘rcppstan’ 似乎init.osrc/Makevars 引用,由rstantools::rstan_package_skeleton 生成
  • 此外,请参阅显示为对 rstan 具有反向 LinkTo 依赖的 10 多个其他软件包:cran.r-project.org/web/packages/rstan
  • @coatless 有两个链接。第一个用于仅包含 Rcpp (rcpptest) 的包,第二个用于包含 Rcpp 和 rstan (rcppstan) 的包。两者都包含在内以说明在添加 rstan 时访问 Rcpp 函数时出现的问题。此外,问题不在于 rstan(我已经编写了其他依赖于 rstan 的软件包),而是同时具有 rstan 和 Rcpp。如果我能进一步澄清,请告诉我。
  • @JakeThompson 尝试将src/Makevars{.win} 中的行从OBJECTS = $(SOURCES:.stan=.o) init.o 更改为OBJECTS = $(SOURCES:.stan=.o)

标签: r rcpp stan rstan


【解决方案1】:

init.cpp 用于注册方法,Makevars 禁止编译 cpp 文件。随着Makevars(和Makevars.win)的以下变化,我得到了编译:

diff --git a/src/Makevars b/src/Makevars
index 7aedc5b..3ea312e 100644
--- a/src/Makevars
+++ b/src/Makevars
@@ -1,8 +1,9 @@
 STANHEADERS_SRC = `"$(R_HOME)/bin$(R_ARCH_BIN)/Rscript" --vanilla -e "cat(system.file('include', 'src', package = 'StanHeaders'))"`
 PKG_CPPFLAGS = -I"../inst/include" -I"$(STANHEADERS_SRC)" -DBOOST_RESULT_OF_USE_TR1 -DBOOST_NO_DECLTYPE -DBOOST_DISABLE_ASSERTS -DEIGEN_NO_DEBUG -DBOOST_MATH_OVERFLOW_ERROR_POLICY=errno_on_error

-SOURCES = $(wildcard stan_files/*.stan)
-OBJECTS = $(SOURCES:.stan=.o) init.o
+CPP_SOURCES = $(wildcard *.cpp)
+STAN_SOURCES = $(wildcard stan_files/*.stan)
+OBJECTS = $(STAN_SOURCES:.stan=.o) $(CPP_SOURCES:.cpp=.o)

 all: $(SHLIB)
                @if test -e "/usr/bin/install_name_tool" && test -e "/usr/local/clang4/lib/libc++.1.dylib" && test -e "/usr/lib/libc++.1.dylib"; then /usr/bin/install_name_tool -change /usr/local/clang4/lib/libc++.1.dylib /usr/lib/libc++.1.dylib $(SHLIB); fi

在调用Rcpp::compileAttributes() 之后,方法注册再次出现在RcppExports.cpp 中。当我尝试R CMD INSTALL 时,我收到了来自.onLoad() 的错误,c.f. https://github.com/stan-dev/rstanarm/issues/190。从那里使用解决方法,即R CMD INSTALL --preclean 首先解决了问题,但不可靠。对我来说解决问题的是改变

R_useDynamicSymbols(dll, FALSE);

进入

R_useDynamicSymbols(dll, TRUE);

RcppExports.cpp。这当然是有问题的,因为该文件可能会被覆盖,尤其是在使用 RStudio/devtools 时。一个相当老套的解决方案是添加

RcppExports.o: patch

patch:
        sed -i 's/R_useDynamicSymbols(dll, FALSE)/R_useDynamicSymbols(dll, TRUE)/' RcppExports.cpp

.phony: all clean patch

Makevars(.win)。但最终问题似乎是 Rcpp 模块的 C++ 代码是在包安装期间在src 的子目录中生成的。因此Rcpp::compileAttributes() 不能在注册方法列表中包含相应的方法。我没有看到一个好的解决方案。 现在,包构建、检查和安装带有两个注释:

N  checking installed package size
   installed size is  7.8Mb
   sub-directories of 1Mb or more:
     libs   7.7Mb
N  checking for GNU extensions in Makefiles
   GNU make is a SystemRequirements.

【讨论】:

  • 谢谢,这很有帮助!不幸的是,与 Stan 接口的软件包需要 .onLoad() 函数,以便能够从其他函数访问 Stan 模型。
  • 如果您在DESCRIPTION 中将GNUmake 添加到SystemRequirements:,则第二个注释应该会消失。
  • @Dirk DESCRIPTION 已经包含 SystemRequirements: GNU make。如果我删除空间,我会因为 GNU make 扩展而收到警告。看来这个 NOTE 是最好的。
  • 啊,是的,也许吧。我需要它已经有一段时间了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-13
  • 1970-01-01
  • 2014-08-28
  • 2013-04-01
  • 2010-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多