Rcpp:无法加载共享对象,未定义符号

Rcpp: unable to load shared object, undefined symbol

本文关键字:对象 未定义 符号 共享 加载 Rcpp      更新时间:2023-10-16

我正试图使用Rcpp将BayesOpt C++库的功能扩展到R中。我是一个长期的R用户,但对C++相对陌生,遇到了一些问题。我已经按照Rcpp的小插曲设置了一个包,我知道这是引入外部C++库的最佳方式。

我已经将src/Makevars中的PKG_CPPFLAGS和PKG_LIBS设置为BayesOpt-include文件夹和库,并且我在src/中有一个.cpp文件(称为test.cpp),它对BayesOpt中的一些头文件使用#include。在这个文件中,我在要导出的函数上方有// [[Rcpp::export]]

当我运行R CMD check mypackage时,库似乎工作得很成功——查看日志,一切都很顺利,直到它尝试加载刚刚"安装"的包。然后,我得到

** testing if installed package can be loaded
Error in dyn.load(file, DLLpath = DLLpath, ...) : 
  unable to load shared object '/home/me/p3/mypackage.Rcheck/mypackage/libs/mypackage.so':
   /home/me/p3/mypackage.Rcheck/mypackage/libs/mypackage.so: undefined symbol: _ZTIN8bayesopt13DiscreteModelE

在错误日志中。echo _ZTIN8bayesopt13DiscreteModelE | c++filt给出了typeinfo for bayesopt::DiscreteModel,这是我的test.cpp文件中使用BayesOpt头的第一个对象。我到处寻找解决方案,但似乎找不到。我相信Makevars正确地指向了库,因为它能够在第一次安装检查期间找到头文件——只有在加载候选包时,我才会得到这个未定义的符号错误。我看过一个使用外部库的Rcpp示例,但Dirk在我看过的答案中指出的RcppGSL有一个3500多行的配置脚本,它填充了Makevars,解析起来有点困难。

我非常感谢任何人的帮助——我的最后手段是将所有内容都转储到src中,但对于一个已经组织整齐的图书馆来说,这似乎很麻烦,也不那么优雅。

不要看自动生成的configure脚本——看看configure.ac,它是它的源,它是所有重要的5行(见下文)加上可能的5行设置和完成。

简而言之,您可能只需要加载标头(通过-I...)和链接(通过-L... -l...)的值。

为此,我们在src/Makevars.in:中这样做

# set by configure
GSL_CFLAGS = @GSL_CFLAGS@
GSL_LIBS   = @GSL_LIBS@
# combine with standard arguments for R
PKG_CPPFLAGS = $(GSL_CFLAGS) -I../inst/include
PKG_LIBS = $(GSL_LIBS) 

@GSL...@表示的两个变量都是通过configure设置的,configure.ac(本质上)只是在断言我们拥有它之后调用gsl-config

## Use gsl-config to find arguments for compiler and linker flags
##
## Check for non-standard programs: gsl-config(1)
AC_PATH_PROG([GSL_CONFIG], [gsl-config])
## If gsl-config was found, let's use it
if test "${GSL_CONFIG}" != ""; then
    # Use gsl-config for header and linker arguments
    GSL_CFLAGS=`${GSL_CONFIG} --cflags`
    GSL_LIBS=`${GSL_CONFIG} --libs`
else
    AC_MSG_ERROR([gsl-config not found, is GSL installed?])
fi

在过去十年左右创建的许多其他库使用一种名为pkg-config的类似(但更通用)工具,该工具具有相同的目的:将编译和链接器标志传达给使用该库的程序。

确实需要两者,您的评论

我相信Makevars正确地指向了库,因为它能够在第一次安装检查时找到头文件

表示您已经整理了编译,但没有链接,或者可能没有库的系统提供。同样,对于RcppGSL,构建过程中的最后一行是以下(为简洁起见进行了编辑)

g++ -shared -L/usr/lib/R/lib -o RcppGSL.so 
 RcppExports.o fastLm.o setErrorHandler.o 
 -L/usr/lib/x86_64-linux-gnu -lgsl -lgslcblas 
 -lm -L/usr/lib/R/lib -lR

它将三个源文件与两个与GSL相关的库以及R和数学库链接起来。您必须在构建中看到类似的内容,否则设置不正确。

编辑:如果将BayesOpt捆绑在包中,则需要将其构建到静态库中,并在src/Makevars中列出。这是一个不同的用例:对于RcppGSL,我们寻找系统GSL安装。本地不同。你可以研究处理这两种情况的nloptr包。