简体   繁体   中英

Undefined reference when building R package using Rcpp with an external C++ library

I'm trying to create a R package for my own use, that is using Rcpp and whose C++ code include the Levmar library. I'm working on Windows.

The C++ code works fine when I build it using CMake for example and run it with Visual Studio. But when I put this code in my R package and try to build it, I get the following error :

levmar_example_r.o:levmar_example_r.cpp:(.text+0x281): undefined reference to `dlevmar_der'

(dlevmar_der is declared in levmar.h which is included in my R package, see below)

I have already read quite a lot of SO posts on how to build a R package with external libraries like this or this but it didn't help me to solve my problem.

The structure of my package :

bin/
  |- levmar.lib
inst/
  |- include/
      |- levmar.h
man/
R/
src/
  |- Makevars
  |- Makevars.win
  |- levmar_example_r.cpp
  |- RcppExports.cpp
src-i386/
DESCRIPTION
NAMESPACE

Content of Makevars/Makevars.win

PKG_LIBS = -L../bin -llevmar
PKG_CPPFLAGS = -I../inst/include 

The C++ code (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;
 }

I have also tried to put all headers of the levmar library in the inst/include folder and all the .c files in a src/levmar folder and consequently remove

PKG_LIBS = -L../bin -llevmar

in Makevars/Makevars.win and add

-I src/levmar

to the PKG_CPPFLAGS but it didn't work out either.

Do you have any idea on what I should do ?

Don't hesitate to ask for precisions if I wasn't clear enough

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 . Important points:

  • Source files in sub-directories of src are not automatically compiled. One has to add them somehow, eg

     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) 
  • By default levmar tries to build single- and double-precision functions and tries to use LAPACK. Default builds of R only include double-precision LAPACK and BLAS. I disabled the single precision build.

  • The levmar library is actually pure C. So my suspicion that your problems where caused by the different C++ ABIs between VC and gcc is probably not correct. Most likely there is some other incompatibility between VC and gcc concerning the layout of static libraries.

Right now the only available function is your test_levmar() . Tested on Linux and Windows (via Appveyor and rhub).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM