barret
barret

Reputation: 61

Compile C++ with embedded Python using CMake Import error

I'm trying to include a python file in a C++ project compiled using CMake.

First I did this standalone using these two files:

#include <Python.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
  setenv("PYTHONPATH", ".", 0);
  char hostname[] = "localhost";
  PyObject *pName, *pModule, *pFunc;
  PyObject *pArgs, *pValue;
  Py_Initialize();
  pName = PyString_FromString("GetHostname");
  pModule = PyImport_Import(pName);
  Py_DECREF(pName);

  if(pModule != NULL) {
    pFunc = PyObject_GetAttrString(pModule, "GetHostname");

    if (pFunc && PyCallable_Check(pFunc)) {
      pArgs = PyTuple_New(1);
      pValue = PyString_FromString(hostname);
      PyTuple_SetItem(pArgs, 0, pValue);
      pValue = PyObject_CallObject(pFunc, pArgs);
      Py_DECREF(pArgs);
      if (pValue != NULL) {
    printf("The IP address is %s\n", PyString_AsString(pValue));
    Py_DECREF(pValue);
      }
      else {
    Py_DECREF(pFunc);
    Py_DECREF(pModule);
    PyErr_Print();
    fprintf(stderr, "Call Failed\n");
    return 1;
      }
    }
    else {
      if (PyErr_Occurred())
    PyErr_Print();
      fprintf(stderr, "Cannot find function\n");
    }
    Py_XDECREF(pFunc);
    Py_DECREF(pModule);
  }
  else {
    PyErr_Print();
    fprintf(stderr, "Failed to load file\n");
    return 1;
  }
  Py_Finalize();
  return 0;
}

And

import socket

def GetHostname(hostname):
    addr = socket.gethostbyname(hostname)
    return addr

When I compile using

g++ $(python-config --cflags) -o test $(python-config --ldflags) ./test.cpp

from how to link python static library with my c++ program everything works fine.

But this is included in a project compiled using CMake and I must be doing something wrong because after compiling it I get

Traceback (most recent call last):
  File "/src/GetHostname.py", line 1, in <module>
    import socket
  File "/usr/lib64/python2.6/socket.py", line 46, in <module>
    import _socket
ImportError: /usr/lib64/python2.6/lib-dynload/_socketmodule.so: undefined symbol: PyExc_ValueError

In CMakeLists.txt I added the lines

find_package( PythonInterp REQUIRED )
find_package( PythonLibs REQUIRED )
include_directories ( ${PYTHON_INCLUDE_DIRS} )
add_library (GetHostname MODULE GetHostname.cc)
target_link_libraries(GetHostname ${PYTHON_LIBRARIES})
CONFIGURE_FILE(${PATH_TO_SOURCE}GetHostname.py ${PATH_TO_BUILD}GetHostname.py COPYONLY)

Based on this thread Python.h: No such file or directory

Everything compiles but the python module fails to load due to the error. Am I not linking the python libraries correctly in CMake?

Any thoughts that can explain why it fails are welcome.

Using Python 2.6

I'm aware of that this can be done in C++ however this is not the only python module that I need to include so rewriting it in C++ is not the answer I'm looking for. Also I know for now the IP address for localhost is known, this is just for testing purposes.

Upvotes: 4

Views: 3138

Answers (1)

barret
barret

Reputation: 61

So I finally found the answer myself.

The problem was that when linking in CMake the linkage with python libraries are only loaded locally (RTLD_LOCAL), which means the actual python script isn't linked with the python libraries, only C++.

To fix this just load the python libraries with global symbol resolution the first thing in your C++ code.

dlopen("libpython2.6.so.1.0", RTLD_NOW | RTLD_NOLOAD | RTLD_GLOBAL);

This is a quick and dirty fix in that it is specific to python2.6, you should make this portable by defining the variable in CMake based on the version installed on the machine.

You will have to import dlfcn to use dlopen:

#include <dlfcn.h>

Upvotes: 2

Related Questions