Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linking c++-class for boost_python in cygwin

I have been using a wrapper for a c++-class for exporting functions to python for a while on linux. Now I wanted to make this available to my coworkers using windows. However, I fail to create a usable boost_python dll for this in cygwin. The problem arises when trying to link a dependent module in another dll, if I instead compile the dependent source into the same dll it works as expected.

I have a created a minimal example displaying the problem:

The setup:

moduleB/moduleB.cpp # The boost wrapper code

#include <python2.7/Python.h>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>

#include "submodule.hpp"

using namespace boost::python;
using namespace testspace;
using namespace std;

struct cModuleB : public SubModuleClass {
    cModuleB(string name, bool boolVar) : SubModuleClass(name,     boolVar) {
    }

    void printFunc(string strVar, list listVar, int nIntVar=-1) {
        vector<int> vecList;
        for (int l=0; l < len(listVar); l++) {
            vecList.push_back(extract<int>(listVar[l]));
        }
        bool bMoreThanHalf = subModuleFunction(vecList);
        if (bMoreThanHalf) {
            cout << "More than half elements are more than 1";
        }
        return;
    }
};

BOOST_PYTHON_MODULE(moduleB)
{
    class_<cModuleB>("cModuleB", init<std::string, bool>())
      .def("printFunc", &cModuleB::printFunc);
}

submodule/submodule.hpp # The submodule containing the c++ class

#include <vector>
#include <string>

using namespace std;

namespace testspace {

   class SubModuleClass {

     public:
            SubModuleClass(string name = "", bool bIsGreat = false);
            ~SubModuleClass();

            bool subModuleFunction(vector<int> & myVec);
   };
}
  • submodule/submodule.cpp # The c++ class definition
using namespace std;

#include "submodule.hpp"

using namespace testspace;

SubModuleClass::SubModuleClass(string name, bool bIsGreat)
{       
}

SubModuleClass::~SubModuleClass()
{               
}   

bool SubModuleClass::subModuleFunction(vector<int> & myVec)
{
    int nCounter = 0;
    for (vector<int>::iterator vi = myVec.begin(); vi != myVec.end(); vi++) {
        if (*vi > 1) nCounter++;
    }
    if (nCounter*2 > (int)myVec.size()) {
        return true;
    }
    else {
        return false;
    }
}

First we compile submodule into a shared library by the following two commands:

g++ -MMD -MP -ffloat-store -m64 -O0 -ggdb -DDEBUG -D_DEBUG \
     -c submodule/submodule.cpp -o submodule/submodule.o

g++ -m64 -shared -Wl,-soname=cygsubmodule_for_moduleB.dll \
    -Wl,--whole-archive submodule/submodule.o -Wl,--no-whole-archive \
    -Wl,--out-implib,./libsubmodule_for_moduleB.dll.a \
    -Wl,--export-all-symbols -Wl,--enable-auto-import \
    -o submodule/cygsubmodule.dll

And the we compile the actual wrapper code and link it into a boost_python dll that we should be able to import from python (verified that the included example works find on ubuntu).

g++ -MMD -MP -ffloat-store -m64 -O0 -ggdb -fPIC  \
    -Isubmodule -I/usr/include/python2.7 -DDEBUG -D_DEBUG \
    -c moduleB/moduleB.cpp -o moduleB/moduleB.o

g++ -m64 -shared -Wl,-soname=cygmoduleB.dll \
    -Wl,--whole-archive moduleB/moduleB.o -Wl,--no-whole-archive \
    -Wl,--out-implib,./libmoduleB.dll.a -Wl,--export-all-symbols \
    -Wl,--enable-auto-import -Lsubmodule -lsubmodule -lstdc++ 
    -lboost_python -lpython2.7 -o moduleB/cygmoduleB.dll

In ubuntu this module can be used as is after removing the cyg-prefix from the .sofile and making sure that the submodule.so is in the LD_LIBRARY_PATH. However, cygwin shows the classic import error:

>>> import moduleB
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No such file or directory

Examining the differences of ldd on the working moduleB between cygwin and Ubuntu one can see that the boost and python dlls have beeen replaced by question marks in the output from moduleB.dll.

moduleB on cygwin:

# ldd moduleB.dll
ntdll.dll => /cygdrive/c/Windows/SYSTEM32/ntdll.dll (0x778b0000)
kernel32.dll => /cygdrive/c/Windows/system32/kernel32.dll (0x77470000)
KERNELBASE.dll => /cygdrive/c/Windows/system32/KERNELBASE.dll (0x7fefdfd0000)
SYSFER.DLL => /cygdrive/c/Windows/System32/SYSFER.DLL (0x75090000)
??? => ??? (0x4f3d00000)

moduleB on Ubuntu:

# ldd moduleB.so
linux-vdso.so.1 =>  (0x00007fff55b73000)
libsubmodule.so => libsubmodule.so (0x00007fee4f9d7000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fee4f6a8000)
libpython2.7.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0 (0x00007fee4f144000)
libboost_python-py27.so.1.55.0 => /usr/lib/x86_64-linux-gnu/libboost_python-py27.so.1.55.0 (0x00007fee4eef7000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fee4ece1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fee4e91b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fee4ddbf000)
/lib64/ld-linux-x86-64.so.2 (0x000055f47c1a6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fee4dba0000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fee4d987000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fee4d783000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fee4d580000)

Any idea as to why moduleB fails to identify itself as a boost_module in cygwin, and why g++ fails to include the appropriate dependency information?

boost_python was installed using the package in cygwin.


UPDATE: In addition to the correct answer below, two things should be noted that obfuscated the correct solution:

  1. ldd on cygwin does not give the same output as on ubuntu and instead of <library>.dll => not foundit only displays something like ? => ? (<address>)
  2. When loading a boost module in python on cygwin that depends on a dll not in path we do not get a message that a dll is missing, only that the boost module can not be found.
like image 865
fnokke Avatar asked Jan 30 '17 16:01

fnokke


People also ask

What are Python bindings for C and C++?

Are you a Python developer with a C or C++ library you’d like to use from Python? If so, then Python bindings allow you to call functions and pass data from Python to C or C++, letting you take advantage of the strengths of both languages.

Do I need to compile the Boost libraries?

After spending some time at searching, we decided to include the Boost libraries, due to the large set of utilities they offer. Most of the C++ Boost libraries are header-only; they consist entirely of header files, and require no separately-compiled library binaries. However, there are some libraries that need to be built separately.

How to build a bootstrap application with Boost libraries?

The Boost libraries includes a really nice build system, which we are definitely going to use it. The build system is triggered from the command line. First we have to open the cmd window and navigate into the root folder of the Boost library. Then we have to initialize the build system by running the bootstrap.bat file.

How do I build the Python bindings?

To build the Python bindings, you need to call .compile (): This wraps things up by generating the .c file, .o file, and the shared library. The invoke task you just walked through can be run on the command line to build the Python bindings:


1 Answers

The following changes must be made:

  1. Link C++ programs and libraries with g++.
  2. Python module name must match file name, so if a file is named moduleB.dll there should be a line that says BOOST_PYTHON_MODULE(moduleB) in there somewhere.
  3. In order to actually load the module, it should be in the Python's sys.path, and all its dependencies should be in the windows %PATH% (or in the current directory, or wherever Windows allows to put dependent DLLs). LD_LIBRARY_PATH doesn't work in Cygwin the way it works in real Unix systems.
like image 76
n. 1.8e9-where's-my-share m. Avatar answered Oct 11 '22 18:10

n. 1.8e9-where's-my-share m.