使用带有外部 C++ 库的 Rcpp 构建 R 包时未定义的引用
Undefined reference when building R package using Rcpp with an external C++ library
我正在尝试创建一个 R 包供我自己使用,它使用 Rcpp 并且其 C++ 代码包含 Levmar 库。我正在研究 Windows。
例如,当我使用 CMake 构建 C++ 代码并使用 Visual Studio 运行 构建它时,C++ 代码工作正常。但是当我将这段代码放入我的 R 包并尝试构建它时,出现以下错误:
levmar_example_r.o:levmar_example_r.cpp:(.text+0x281): undefined reference to `dlevmar_der'
(dlevmar_der 在我的 R 包中包含的 levmar.h 中声明,见下文)
我已经阅读了很多关于如何使用 or 等外部库构建 R 包的 SO 帖子,但它并没有帮助我解决我的问题。
我的包的结构:
bin/
|- levmar.lib
inst/
|- include/
|- levmar.h
man/
R/
src/
|- Makevars
|- Makevars.win
|- levmar_example_r.cpp
|- RcppExports.cpp
src-i386/
DESCRIPTION
NAMESPACE
Makevars/Makevars.win
的内容
PKG_LIBS = -L../bin -llevmar
PKG_CPPFLAGS = -I../inst/include
C++代码(levmar_example_r.cpp)
#include <iostream>
#include <levmar.h>
#include <math.h>
#include <Rcpp.h>
void fun(double *p, double *x, int m, int n, void *data_){
double a = p[0];
double b = p[1];
double *data = (double *) data_;
for(int i = 0; i < n; i++){
x[i] = log(a*data[i]+b);
}
}
void jacFun(double *p, double *jac, int m, int n, void *data_){
double a = p[0];
double b = p[1];
double *data = (double *) data_;
int k, l;
for(l=k=0; l < n; l++){
jac[k++] = data[l]/(a*data[l]+b);
jac[k++] = 1/(a*data[l]+b);
}
}
// [[Rcpp::export]]
void test_levmar(){
int m = 2; // # of parameters
int n = 40; // # of observations
double a = 1.0;
double b = 2.0;
double data[] = {0.119047619047619, 0.238095238095238, 0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143,
0.119047619047619 ,0.238095238095238, 0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714 ,1.07142857142857, 1.42857142857143 ,
0.119047619047619, 0.238095238095238, 0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143,
0.119047619047619, 0.238095238095238, 0.357142857142857, 0.476190476190476 ,0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143,
0.119047619047619, 0.238095238095238 ,0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143};
double popti[2];
popti[0] = a; popti[1] = b;
double x[40];
fun(popti, x, m, n, (void *) data);
// algorithm parameters
double opts[LM_OPTS_SZ], info[LM_INFO_SZ];
opts[0]=LM_INIT_MU;
// stopping thresholds for
opts[1]=1E-10; // ||J^T e||_inf
opts[2]=1E-10; // ||Dp||_2
opts[3]=1E-10; // ||e||_2
opts[4]= LM_DIFF_DELTA; // finite difference if used
double p[2];
p[0] = 3.0; p[1] = 1.0;
dlevmar_der(fun,jacFun,p,x,m,n,100,opts,info,NULL,NULL,(void *) data);
std::cout << "Optimum found:" << std::scientific << std::setprecision(8)<< "\t"<< p[0]<< "\t" << p[1]<< std::endl;
}
我还尝试将所有 headers 的 levmar 库放在 inst/include 文件夹中,将所有 .c 文件放在 src/levmar 文件夹中,然后删除
PKG_LIBS = -L../bin -llevmar
在 Makevars/Makevars.win 中添加
-I src/levmar
到PKG_CPPFLAGS,但也没有成功。
你知道我该怎么做吗?
如果我不够清楚,请不要犹豫,要求精确度
SODD got the better of me. I have build a very rough package that compiles the levmar code and creates an initial R package from it: https://github.com/rstub/levmaR。要点:
src
子目录下的源文件不会自动编译。必须以某种方式添加它们,例如
CXX_STD = CXX11
PKG_LIBS=-L. -llevmar $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS)
PKG_CPPFLAGS=-I./levmar/ -DSTRICT_R_HEADERS
all: $(SHLIB)
$(SHLIB): liblevmar.a
LIBOBJS = levmar/lm.o levmar/Axb.o levmar/misc.o levmar/lmlec.o levmar/lmbc.o \
levmar/lmblec.o levmar/lmbleic.o
liblevmar.a: $(LIBOBJS)
$(AR) rcs liblevmar.a $(LIBOBJS)
默认情况下,levmar 尝试构建单精度和双精度函数并尝试使用 LAPACK。 R 的默认构建仅包括双精度 LAPACK 和 BLAS。我禁用了单精度构建。
levmar 库实际上是纯 C 语言。所以我怀疑您的问题是由 VC 和 gcc 之间的不同 C++ ABI 引起的可能是不正确的。 VC 和 gcc 之间很可能存在一些其他关于静态库布局的不兼容性。
目前唯一可用的功能是您的test_levmar()
。在 Linux 和 Windows 上测试(通过 Appveyor 和 rhub)。
我正在尝试创建一个 R 包供我自己使用,它使用 Rcpp 并且其 C++ 代码包含 Levmar 库。我正在研究 Windows。
例如,当我使用 CMake 构建 C++ 代码并使用 Visual Studio 运行 构建它时,C++ 代码工作正常。但是当我将这段代码放入我的 R 包并尝试构建它时,出现以下错误:
levmar_example_r.o:levmar_example_r.cpp:(.text+0x281): undefined reference to `dlevmar_der'
(dlevmar_der 在我的 R 包中包含的 levmar.h 中声明,见下文)
我已经阅读了很多关于如何使用
我的包的结构:
bin/
|- levmar.lib
inst/
|- include/
|- levmar.h
man/
R/
src/
|- Makevars
|- Makevars.win
|- levmar_example_r.cpp
|- RcppExports.cpp
src-i386/
DESCRIPTION
NAMESPACE
Makevars/Makevars.win
的内容PKG_LIBS = -L../bin -llevmar
PKG_CPPFLAGS = -I../inst/include
C++代码(levmar_example_r.cpp)
#include <iostream>
#include <levmar.h>
#include <math.h>
#include <Rcpp.h>
void fun(double *p, double *x, int m, int n, void *data_){
double a = p[0];
double b = p[1];
double *data = (double *) data_;
for(int i = 0; i < n; i++){
x[i] = log(a*data[i]+b);
}
}
void jacFun(double *p, double *jac, int m, int n, void *data_){
double a = p[0];
double b = p[1];
double *data = (double *) data_;
int k, l;
for(l=k=0; l < n; l++){
jac[k++] = data[l]/(a*data[l]+b);
jac[k++] = 1/(a*data[l]+b);
}
}
// [[Rcpp::export]]
void test_levmar(){
int m = 2; // # of parameters
int n = 40; // # of observations
double a = 1.0;
double b = 2.0;
double data[] = {0.119047619047619, 0.238095238095238, 0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143,
0.119047619047619 ,0.238095238095238, 0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714 ,1.07142857142857, 1.42857142857143 ,
0.119047619047619, 0.238095238095238, 0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143,
0.119047619047619, 0.238095238095238, 0.357142857142857, 0.476190476190476 ,0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143,
0.119047619047619, 0.238095238095238 ,0.357142857142857, 0.476190476190476, 0.595238095238095, 0.714285714285714, 1.07142857142857, 1.42857142857143};
double popti[2];
popti[0] = a; popti[1] = b;
double x[40];
fun(popti, x, m, n, (void *) data);
// algorithm parameters
double opts[LM_OPTS_SZ], info[LM_INFO_SZ];
opts[0]=LM_INIT_MU;
// stopping thresholds for
opts[1]=1E-10; // ||J^T e||_inf
opts[2]=1E-10; // ||Dp||_2
opts[3]=1E-10; // ||e||_2
opts[4]= LM_DIFF_DELTA; // finite difference if used
double p[2];
p[0] = 3.0; p[1] = 1.0;
dlevmar_der(fun,jacFun,p,x,m,n,100,opts,info,NULL,NULL,(void *) data);
std::cout << "Optimum found:" << std::scientific << std::setprecision(8)<< "\t"<< p[0]<< "\t" << p[1]<< std::endl;
}
我还尝试将所有 headers 的 levmar 库放在 inst/include 文件夹中,将所有 .c 文件放在 src/levmar 文件夹中,然后删除
PKG_LIBS = -L../bin -llevmar
在 Makevars/Makevars.win 中添加
-I src/levmar
到PKG_CPPFLAGS,但也没有成功。
你知道我该怎么做吗?
如果我不够清楚,请不要犹豫,要求精确度
SODD got the better of me. I have build a very rough package that compiles the levmar code and creates an initial R package from it: https://github.com/rstub/levmaR。要点:
src
子目录下的源文件不会自动编译。必须以某种方式添加它们,例如CXX_STD = CXX11 PKG_LIBS=-L. -llevmar $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) PKG_CPPFLAGS=-I./levmar/ -DSTRICT_R_HEADERS all: $(SHLIB) $(SHLIB): liblevmar.a LIBOBJS = levmar/lm.o levmar/Axb.o levmar/misc.o levmar/lmlec.o levmar/lmbc.o \ levmar/lmblec.o levmar/lmbleic.o liblevmar.a: $(LIBOBJS) $(AR) rcs liblevmar.a $(LIBOBJS)
默认情况下,levmar 尝试构建单精度和双精度函数并尝试使用 LAPACK。 R 的默认构建仅包括双精度 LAPACK 和 BLAS。我禁用了单精度构建。
levmar 库实际上是纯 C 语言。所以我怀疑您的问题是由 VC 和 gcc 之间的不同 C++ ABI 引起的可能是不正确的。 VC 和 gcc 之间很可能存在一些其他关于静态库布局的不兼容性。
目前唯一可用的功能是您的test_levmar()
。在 Linux 和 Windows 上测试(通过 Appveyor 和 rhub)。