24

First off: I looked at the related questions, but they are not very helpful unfortunately. I'm trying to wrap an enum and a class from an external library.

#include <Python.h>
#include <boost/python.hpp>
using namespace boost::python;

#include <libvpsc/rectangle.h>
using vpsc::Rectangle;
using vpsc::Dim;

BOOST_PYTHON_MODULE(adaptagrams)
{
    enum_<Dim>("dim")
        .value("x", vpsc::XDIM)
        .value("y", vpsc::YDIM)
        .value("unset", vpsc::UNSET)
    ;

    class_<Rectangle>("Rectangle",
        init<double, double, double, double, optional<bool> >())

        .add_property("centerX", &Rectangle::getCentreX)
        .add_property("centerY", &Rectangle::getCentreY)
        .add_property("width", &Rectangle::width, &Rectangle::set_width)
        .add_property("height", &Rectangle::height, &Rectangle::set_height)
    ;
}

and compile with:

g++ -fPIC -I/usr/include/python2.7 -c adaptagrams.cpp -o adaptagrams.o
g++ -shared -Wl,-soname,adaptagrams.so -o adaptagrams.so adaptagrams.o -lpython2.7  -lboost_python -lvpsc

However, when I try to import the .so module, I get an error:

ImportError: dynamic module does not define init function (PyInit_adaptagrams)

Any ideas?

Update: When I restart Python and try the import, the first error I get is:

ImportError: ./adaptagrams.so: undefined symbol: _ZN8topology13computeStressERKSt6vectorIPNS_4EdgeESaIS2_EE

When I try it again, the 2nd one is the dynamic import from above (2.7) and a segfault (3.2). Boost is compiled against both 2.7 and 3.2 and I am linking the right ones on each approach.

Update 2: The tutorial code from the boost_python page works:

#include <Python.h>
#include <boost/python.hpp>
using namespace boost::python;

struct Hello
{
    Hello(std::string msg): msg(msg) {}
    void set(std::string msg) { this->msg = msg; }
    std::string greet() { return msg; }
    std::string msg;
};

BOOST_PYTHON_MODULE(constructor)
{
    class_<Hello>("Hello", init<std::string>())
        .def("greet", &Hello::greet)
        .def("set", &Hello::set)
    ;
}

Same compilation:

g++ -fPIC -I/usr/include/python2.7 -c constructor.cpp -o constructor.o
g++ -shared -Wl,-soname,constructor.so -o constructor.so constructor.o -lpython2.7 -lboost_python
Michael Schubert
  • 2,726
  • 4
  • 27
  • 49

3 Answers3

83

The name used in BOOST_PYTHON_MODULE must match the name of the .so library you generate and import into python.

ridoy
  • 6,274
  • 2
  • 29
  • 60
jgoeders
  • 1,886
  • 19
  • 25
  • do you know where in the documentation this is stated? – ofloveandhate Sep 15 '15 at 21:30
  • 1
    Interesting: I had to do `BOOST_PYTHON_MODULE(libhello)` whereas my CMake settings called my library 'hello'. This seems to conflict with the Boost.Python docs. – mbr0wn Jan 18 '17 at 01:29
  • The reason for this is that cmake usually prepends "lib". This can be changed when you set the properties of the library target, more specifically when you change the [PREFIX](https://cmake.org/cmake/help/v3.4/prop_tgt/PREFIX.html) property. – fotNelton Aug 08 '17 at 09:12
5

I have seen this exception before. I got it using Visual Studio on windows, so things might be a little different over in unix-oid land but:

Two Possibilities:

Debug/Release miss-match: You are trying to import a debug build of your module into a release build of python (or vice-versa). The solution is to include boost/python/detail/wrap_python.hpp instead of Python.h. This will fix some includes and defines to make it possible to do what you want.

Python/Boost.Python version miss-match: Boost.Python is compiled against one specific version of python. You are using it with a different version. For example: you seem to be using python 2.7. Your boost_python library might be compiled against python 2.6. Yes, this means that your module can only work with one version of python at a time.

Matthew Scouten
  • 15,303
  • 9
  • 33
  • 50
1

In addition to the other answers (in case another unfortunate soul runs unto this), make sure you're not accidentally compiling with the -fvisibility=hidden flag.

Doing so seems to strip the init function from the binary in both g++ / clang++.

Background info

In my case I had some trouble integrating some wrappers made with Boost.Python into a project. When built with the project's build-system I'd get the same runtime-error as OP (as opposed to building it with my proof-of-concept Makefile where it worked just fine).

Comparing the symbol tables with nm -g foo.so | grep Py showed me that in the non-working case the PyInit_* function was completely absent. Doing some comparison between compilation flags led me to -fvisibilty=hidden being the culprit.

tvl
  • 11
  • 1