связать модуль Cython в программе на C ++ - PullRequest
0 голосов
/ 04 декабря 2018

Можно ли построить модуль Cython с некоторыми cdef функциями и связать полученную общую библиотеку с программой на C ++?

Я попробовал проверить концепцию:

cymod.pyx:

# distutils: language=c++

from libcpp.string cimport string

cdef public string simple_echo(string test_string):
    return test_string

cpp_test.cpp:

#define PyMODINIT_FUNC void
#include <iostream>
#include "cymod.h"

int main(int argc, char const *argv[])
{
    std::cout << simple_echo("test") << std::endl;
    return 0;
}

setup.py:

from setuptools import setup, Extension
from Cython.Build import cythonize

setup(
    name='cymod',
    ext_modules=cythonize(
        Extension(
            "cymod", ["cymod.pyx"],
        ),
    )
)

Модуль Cython работает нормально, но когда я пытаюсь собрать C ++код, который будет использовать функцию Cython, я получаю:

$ g++ -L. -l:cymod.so cpp_test.cpp -o cpp_test
/tmp/cc48Vc2z.o: In function `main':
cpp_test.cpp:(.text+0x51): undefined reference to `simple_echo'
collect2: error: ld returned 1 exit status

Что странно.Сгенерированный заголовочный файл имеет его:

cymod.h:

  /* Generated by Cython 0.29.1 */

  #ifndef __PYX_HAVE__cymod
  #define __PYX_HAVE__cymod


  #ifndef __PYX_HAVE_API__cymod

  #ifndef __PYX_EXTERN_C
    #ifdef __cplusplus
      #define __PYX_EXTERN_C extern "C"
    #else
      #define __PYX_EXTERN_C extern
    #endif
  #endif

  #ifndef DL_IMPORT
    #define DL_IMPORT(_T) _T
  #endif

  __PYX_EXTERN_C std::string simple_echo(std::string);

  #endif /* !__PYX_HAVE_API__cymod */

  /* WARNING: the interface of the module init function changed in CPython 3.5. */
  /* It now returns a PyModuleDef instance instead of a PyModule instance. */

  #if PY_MAJOR_VERSION < 3
  PyMODINIT_FUNC initcymod(void);
  #else
  PyMODINIT_FUNC PyInit_cymod(void);
  #endif

  #endif /* !__PYX_HAVE__cymod */

, и я вижу свою функцию в cymod.so:

nm cymod.so| grep simple_echo
0000000000001e50 T simple_echo

ПРИМЕЧАНИЕ. Я понимаю, чточтобы на самом деле это заработало, мне нужно будет связать себя с библиотеками python и инициализировать интерпретатор и т.д.

1 Ответ

0 голосов
/ 04 декабря 2018

Короткий ответ: я слишком рано поставил аргумент -l в команде компиляции.Также важно обрабатывать путь поиска в библиотеке.Самый простой способ - использовать rpath.Я установил rpath в каталог, в котором находится исполняемый файл, т. Е. .

. Кроме того, необходимо установить связь с библиотеками python и задать пути включения и пути к библиотекам.Их можно определить во время компиляции, используя выходные данные утилиты python-config.Вот команда компиляции, которая в конечном итоге добилась цели:

g++ cpp_test.cpp -o cpp_test -L. -l:cymod.so $(python-config --libs) $(python-config --includes) $(python-config --cflags) -Wl,-rpath,"\$ORIGIN"

Я также обновил файл c ++ до #include "Python.h" и добавил вызовы к Py_Initialize(), Py_Finalize() и initcymod():

#include <iostream>
#include "Python.h"
#include "cymod.h"

int main(int argc, char *argv[])
{
    Py_Initialize();
    initcymod();
    std::cout << simple_echo("test") << std::endl;
    Py_Finalize();
    return 0;
}

ПРИМЕЧАНИЕ: вызов initcymod() необходим, но зависит от python2.На python3 вы должны позвонить PyImport_AppendInittab("cymod", PyInit_cymod); до Py_Initialize().cymod часть - это имя модуля, замените имя вашего модуля.

Спасибо @ead за информативную ссылку на документы по этой теме https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#using-cython-declarations-from-c и его ответ на связанный вопрос https://stackoverflow.com/a/45424720/2069572

Читая связанные документы, я наткнулся на следующее:

Примечание. В некоторых операционных системах, таких как Linux, также возможно сначала создать расширение Cython обычным способом изатем ссылка на полученный .so-файл, как динамическая библиотека.Остерегайтесь, что это не переносимо, поэтому его следует избегать.

Так что получается, что вы не должны делать то, что я пытался сделать.

Вместо того, что я должен был иметьсделано было выполнено:

cython --cplus cymod.pyx

и затем скомпилировано cpp_test.cpp с созданным файлом cymod.cpp.Нет необходимости связывать разделяемую библиотеку Cython, и оказывается, что это не очень хорошая идея.

...