关于C#:R包与Rcpp的链接错误:”未定义符号:LAPACKE_dgels”

Linking error of R package with Rcpp: “undefined symbol: LAPACKE_dgels”

我正在创建一个R包" lapacker",以使用R API头文件" R_ext / Lapack.h"为R提供和使用的内部LAPACK库提供C接口(仅具有双精度和双复数)。源代码:
https://github.com/ypan1988/lapacker/

项目结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/lapacker
  /inst
    /include
      /lapacke.h
      /someother header files
  /R
    /zzz.R
  /src
    /lapacke_dgetrf.c
    /lapacke_dgetrf_work.c
    /loads of other utility functions provided by LAPACKE
    /rcpp_hello.cpp
  DESCRIPTION
  NAMESPACE

在项目内部,我尝试了rcpp_hello.cpp文件中的测试功能(请注意,此示例来自https://www.netlib.org/lapack/lapacke.html#_calling_code_dgels_code):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//'@export
// [[Rcpp::export]]
void example_lapacke_dgels()
{
  double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
  double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
  lapack_int info,m,n,lda,ldb,nrhs;
  int i,j;

  m = 5;
  n = 3;
  nrhs = 2;
  lda = 3;
  ldb = 2;

  info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);

  for(i=0;i<n;i++)
  {
    for(j=0;j<nrhs;j++)
    {
      printf("%lf",b[i][j]);
    }
    printf("\
"
);
  }
}

整个程序包可以正确编译而没有错误,并且在R中给出正确的答案(可以找到符号LAPACKE_dgels):

1
2
3
4
> example_lapacke_dgels()
2.000000 1.000000
1.000000 1.000000
1.000000 2.000000

但是,当我创建一个单独的C文件时,请说出具有完全相同功能的demo3.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <Rcpp.h>

#include <lapacke.h>
// [[Rcpp::depends(lapacker)]]

// [[Rcpp::export]]
void lapacke_dgels_test()
{
  double a[5][3] = {{1,1,1},{2,3,4},{3,5,2},{4,2,5},{5,4,3}};
  double b[5][2] = {{-10,-3},{12,14},{14,12},{16,16},{18,16}};
  lapack_int info,m,n,lda,ldb,nrhs;
  int i,j;

  m = 5;
  n = 3;
  nrhs = 2;
  lda = 3;
  ldb = 2;

  info = LAPACKE_dgels(LAPACK_ROW_MAJOR,'N',m,n,nrhs,*a,lda,*b,ldb);

  for(i=0;i<n;i++)
  {
    for(j=0;j<nrhs;j++)
    {
      printf("%lf",b[i][j]);
    }
    printf("\
"
);
  }
}

它不再正确编译(实际上我在macOS和ubuntu下都尝试过,存在相同的链接问题),并给出了链接错误消息(找不到符号LAPACKE_dgels):

1
2
3
4
5
6
7
> Rcpp::sourceCpp("~/Desktop/demo3.cpp", showOutput = TRUE)
/usr/lib/R/bin/R CMD SHLIB -o 'sourceCpp_6.so'  'demo3.cpp'  
g++  -I/usr/share/R/include -DNDEBUG   -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/Rcpp/include" -I"/home/yipan/R/x86_64-pc-linux-gnu-library/3.4/lapacker/include" -I"/home/yipan/Desktop"    -fpic  -g -O2 -fdebug-prefix-map=/build/r-base-AitvI6/r-base-3.4.4=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g  -c demo3.cpp -o demo3.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o sourceCpp_6.so demo3.o -L/usr/lib/R/lib -lR
Error in dyn.load("/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so") :
  unable to load shared object '/tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so':
  /tmp/RtmpUsASwK/sourceCpp-x86_64-pc-linux-gnu-1.0.0/sourcecpp_159e6145591d/sourceCpp_6.so: undefined symbol: LAPACKE_dgels

我还检查了/R/x86_64-pc-linux-gnu-library/3.4/lapacker/libs下的lapacker.so,发现:

1
000000000000c6b0 g    DF .text  00000000000001bf  Base        LAPACKE_dgels

我错过了一些东西来使demo3.cpp正确编译吗?非常感谢您的耐心和时间!


您在这里面临着一个难题。您尝试解析的LAPACKE_dgels符号是lapacker.so的一部分,是在软件包安装期间生成的。问题是R软件包的库不用于链接。取而代之的是,它们在运行时由R动态加载。基本上,我看到四种可能性:

  • lapacke转换为仅标头的库,并将其安装在inst/include中(参见RcppArmadillo)。
  • lapacke的系统安装链接(在Linux上很容易...)
  • 向R注册所有功能,并使用R提供的方法链接到它们(参见WRE和nloptr)。
  • 编译用于链接的库,并将其与R软件包一起安装。您仍然需要一个插件才能正常工作,因为您必须在PKG_LIBS中添加-L<path/to/lib> -l<libname> ....
  • 我确定CRAN上有使用方法4的示例,但现在还没有想到。但是,作为"代码kata",我已经转换了我最近的测试包以使用此结构c.f。 https://github.com/rstub/levmaR/tree/static。

    (原始不完整答案。)

    src/Makevars中您具有

    1
    PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)

    通过Rcpp属性编译cpp文件时,需要模拟设置。最好的方法是使用Rcpp插件c.f。 RcppArmadillo的解决方案(调整未经测试!):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    inlineCxxPlugin <- function(...) {
        plugin <-
            Rcpp::Rcpp.plugin.maker(
                      include.before ="#include <lapacke.h>",
                      libs           ="$(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)",
                      package        ="lapacker"
                  )
        settings <- plugin()
        settings$env$PKG_CPPFLAGS <-"-I../inst/include"
        settings
    }

    顺便说一句,当RcppArmadillo已经这样??做时,为什么要直接与LAPACK交互?