8
votes

I'm trying to use Rcpp to extend functionality from the BayesOpt C++ library into R. I'm a long-time R user but relatively new to C++ and I'm running into some problems. I've followed the Rcpp vignettes for setting up a package, which I understand is the best way to bring external C++ libraries in.

I've set PKG_CPPFLAGS and PKG_LIBS in src/Makevars to the BayesOpt include folders and library, and I have a single .cpp file (call it test.cpp) in src/ that uses #include for some header files from BayesOpt. In this file, I have // [[Rcpp::export]] above the function I want to export.

When I run R CMD check mypackage, the library seems to be working successfully -- looking at the log, everything goes well until it tries to load the package that was just "installed". Then, I get

** 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

in the error log. echo _ZTIN8bayesopt13DiscreteModelE | c++filt gives typeinfo for bayesopt::DiscreteModel, which is the first object in my test.cpp file that uses the BayesOpt headers. I've looked high and low for a solution to this, but I can't seem to find one. I would like to believe that the Makevars points at the library correctly, because it is able to find the header files during the first installation check -- it's only when loading the candidate package that I get this undefined symbol error. I've looked at an Rcpp example that uses external libraries, but the one Dirk points to in the answers I've looked at, RcppGSL, has a 3500+ line configure script that fills out the Makevars, and it's a little difficult to parse.

I would appreciate anyone's help -- my last resort is to dump everything into src but that seems cumbersome and less than elegant for a library that is already neatly organized.

1

1 Answers

7
votes

Don't look at the configure script which is auto-generated -- look at configure.ac which is its source, and which is all of 5 lines which matter (see below) plus maybe 5 lines of setup and finish.

And in a nutshell, you may just need to load values for both headers (via -I...) and linking (via -L... -l...).

And for that we do this in 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) 

The two variables denoted by @GSL...@ are both set via configure and configure.ac (in essence) just calls gsl-config after asserting that we have it:

## 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

Many other libraries created in the last decade or so use a similar (but more generic) tool called pkg-config which serves the same purpose: communicate the compile and linker flags to programs using the library.

And you do need both, your comment

I would like to believe that the Makevars points at the library correctly, because it is able to find the header files during the first installation check

indicates you have the compilation sorted out but not the linking or maybe not the system provision of your library. Again, for RcppGSL the final line during build is the following (edited for brevity)

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

It links three source files with two GSL-related libraries as well with R and the math library. You must see something similar in your build, or it is not correctly setup.

Edit: And if you bundle BayesOpt in your package, then you need to build it into a static library and list that in the src/Makevars. That is a different use case: for RcppGSL we look for a system GSL installation. Local is different. You could study the nloptr package which deals with both cases.