Question

I have a simple C++ program that I can build successfully using clang++ on my Mac (Mavericks) but that fails when built with R CMD SHLIB and loaded with dyn.load in R.

This is the C++ code (stored in simple.cpp), which uses the Gurobi optimizer:

#include "gurobi_c++.h"
#include <iostream>

void fxn() {
  GRBEnv env = GRBEnv();  // Create a Gurobi environment
  GRBModel colgen = GRBModel(env);  // Create empty model object
  colgen.addVar(0, 1, 0.0, GRB_BINARY);  // Add binary variable to model
  std::cout << "Hello world" << std::endl;
}

int main(int argc, char **argv) {
  fxn();
  return 0;
}

I can successfully compile and run this code using clang++, linking to the Gurobi libraries:

$ clang++ simple.cpp -I/Library/gurobi562/mac64/include \
  -L/Library/gurobi562/mac64/lib -lgurobi_c++ -lgurobi56 -stdlib=libstdc++ \
  -lpthread -lm
$ ./a.out
Hello world

I am able to compile successfully with R CMD SHLIB:

$ MAKEFLAGS="PKG_CXXFLAGS=-I/Library/gurobi562/mac64/include" R CMD SHLIB \
  simple.cpp -L/Library/gurobi562/mac64/lib -lgurobi_c++ -lgurobi56 \
  -stdlib=libstdc++ -lpthread -lm
clang++ -I/Library/Frameworks/R.framework/Resources/include -DNDEBUG \
  -I/usr/local/include   -I/Library/gurobi562/mac64/include -fPIC  -mtune=core2 \
  -g -O2  -c simple.cpp -o simple.o
clang++ -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup \
  -single_module -multiply_defined suppress -L/usr/local/lib -L/usr/local/lib \
  -o simple.so simple.o -L/Library/gurobi562/mac64/lib -lgurobi_c++ -lgurobi56 \
  -stdlib=libstdc++ -lpthread -lm -F/Library/Frameworks/R.framework/.. \
  -framework R -Wl,-framework -Wl,CoreFoundation

However, dyn.load("simple.so") fails in R:

Error in dyn.load("simple.so") : 
  unable to load shared object '[path]/simple.so':
  dlopen([path]/simple.so, 6): Symbol not found: __ZN8GRBModel6addVarEdddcNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEE
  Referenced from: [path]/simple.so
  Expected in: flat namespace
 in [path]/simple.so

From c++filt, I can see that the missing symbol is GRBModel::addVar(double, double, double, char, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >), which should be provided by one of the Gurobi libraries I'm linking.

From previous posts, I've gathered that these "Symbol not found" errors often occur from simply not linking the correct libraries, but I've been able to successfully compile and run simple.cpp and I'm passing the same linking options to R CMD SHLIB.

The following are the contents of my ~/.R/Makevars file:

CC=clang
CXX=clang++

Edit I think the issue might have to do with the option -stdlib=libstdc++ that I'm using when I compile the code. When I remove this option from the first build (the working invocation of clang++), the first linker error I get is:

Undefined symbols for architecture x86_64:
  "GRBModel::addVar(double, double, double, char, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >)", referenced from:
      _fxn in simple-pRHAEs.o

This is the same undefined symbol that's causing dyn.load to fail.

Was it helpful?

Solution

I solved this issue, which turned out to be related to how I was providing the argument -stdlib=libstdc++ to R CMD SHLIB. R CMD SHLIB invokes clang++ twice, first as a compilation phase to build an object file (simple.o in my case) and then to link that file into a shared object (simple.so in my case). R CMD SHLIB was only passing the -stdlib=libstdc++ argument to the second invocation, but we need it to also provide the argument to the first invocation of clang++. We can do this by adding -stdlib=libstdc++ to PKG_CXXFLAGS:

$ PKG_CXXFLAGS="-I/Library/gurobi562/mac64/include -stdlib=libstdc++" R CMD SHLIB \
  simple.cpp -L/Library/gurobi562/mac64/lib -lgurobi_c++ -lgurobi56 \
  -stdlib=libstdc++ -lpthread -lm
clang++ -I/Library/Frameworks/R.framework/Resources/include -DNDEBUG  \
  -I/usr/local/include   -I/Library/gurobi562/mac64/include -stdlib=libstdc++ \
  -fPIC  -mtune=core2 -g -O2  -c simple.cpp -o simple.o
clang++ -dynamiclib -Wl,-headerpad_max_install_names -undefined dynamic_lookup \
  -single_module -multiply_defined suppress -L/usr/local/lib -L/usr/local/lib \
  -o simple.so simple.o -L/Library/gurobi562/mac64/lib -lgurobi_c++ -lgurobi56 \
  -stdlib=libstdc++ -lpthread -lm -F/Library/Frameworks/R.framework/.. \
  -framework R -Wl,-framework -Wl,CoreFoundation

Now, dyn.load("simple.so") works without error from R (though as mentioned by @MartinMorgan and @JanvanderLaan, I'll need to expose my functions using extern "C" or an alternative to actually be able to call them from R).

OTHER TIPS

R is expecting C linkage, but you've provided C++ linkage. One solution is

#include <iostream>

extern "C" void fxn() {
    std::cout << "Hello world" << std::endl;
}

with

$ R --vanilla CMD SHLIB tmp.cpp && R --vanilla -e "dyn.load('tmp.so'); .C('fxn')"
clang++ -I/home/mtmorgan/bin/R-devel/include -DNDEBUG  -I/usr/local/include    -fpic  -ggdb -O0 -c tmp.cpp -o tmp.o
clang++ -shared -L/usr/local/lib -o tmp.so tmp.o -L/home/mtmorgan/bin/R-devel/lib -lR
> dyn.load('tmp.so'); .C('fxn')
Hello world
list()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top