简体   繁体   English

在c ++程序中链接cython模块

[英]link cython module in a c++ program

Is it possible to build a cython module with some cdef functions and link the resulting shared library into a C++ program? 是否可以使用一些cdef函数构建cython模块并将生成的共享库链接到C ++程序中?

I tried a proof of concept: 我尝试了一个概念验证:

cymod.pyx: cymod.pyx:

# distutils: language=c++

from libcpp.string cimport string

cdef public string simple_echo(string test_string):
    return test_string

cpp_test.cpp: 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: setup.py:

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

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

The cython module builds fine, but when I try to build the c++ code that will use the cython function I get: cython模块构建正常,但是当我尝试构建将使用cython函数的c ++代码时,我得到:

$ 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

Which is odd. 这很奇怪。 The generated header file has it: 生成的头文件包含它:

cymod.h: 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 */

and I see my function in cymod.so : 我在cymod.so看到了我的功能:

nm cymod.so| grep simple_echo
0000000000001e50 T simple_echo

NOTE: I realize that to actually get this working I'll need to link against the python libraries and initialize the interpreter etc. I left that out to make this a tad shorter and I get the same error either way. 注意:我意识到要真正实现这个工作,我需要链接python库并初始化解释器等。我把它留下来使这一点更短,我得到相同的错误。

The short answer is that I was putting the -l argument too early in the compilation command. 简短的回答是我在编译命令中过早地放置-l参数。 It is also important to handle the library lookup path. 处理库查找路径也很重要。 The simplest way is to use rpath . 最简单的方法是使用rpath I set the rpath to the directory that the executable is in, ie, . 我将rpath设置为可执行文件所在的目录,即.

Additionally, it is necessary to link against the python libraries and set the include and library paths. 此外,有必要链接python库并设置包含和库路径。 These can be determined at compile time by using the output of the python-config utility. 这些可以在编译时通过使用python-config实用程序的输出来确定。 Here is the compilation command that ultimately did the trick: 这是最终完成这个技巧的编译命令:

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

I also updated the c++ file to #include "Python.h" and added calls to Py_Initialize() , Py_Finalize() , and initcymod() : 我还将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;
}

NOTE: the call to initcymod() is necessary, but python2 specific. 注意:调用initcymod()是必要的,但python2是特定的。 On python3 you should call PyImport_AppendInittab("cymod", PyInit_cymod); 在python3上你应该调用PyImport_AppendInittab("cymod", PyInit_cymod); prior to Py_Initialize() . Py_Initialize()之前。 The cymod part is the module name, substitute your module name. cymod部分是模块名称,替换您的模块名称。

Thanks to @ead for the informative link to the docs on this topic https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#using-cython-declarations-from-c and his answer to a related question https://stackoverflow.com/a/45424720/2069572 感谢@ead提供有关此主题的文档的信息链接https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#using-cython-declarations-from-c以及他对相关问题https://stackoverflow.com/a/45424720/2069572

While reading the linked docs, I came across this: 在阅读链接文档时,我遇到了这个:

Note On some operating systems like Linux, it is also possible to first build the Cython extension in the usual way and then link against the resulting .so file like a dynamic library. 注意在某些操作系统(如Linux)上,也可以首先以常规方式构建Cython扩展,然后将链接到生成的.so文件,如动态库。 Beware that this is not portable, so it should be avoided. 请注意,这不是便携式的,所以应该避免。

So it turns out that you should not do what I was trying to do. 事实证明,你不应该做我想做的事情。

Instead, what I should have done was run: 相反,我应该做的是运行:

cython --cplus cymod.pyx

And then compiled cpp_test.cpp with the generated cymod.cpp file. 然后用生成的cymod.cpp文件编译cpp_test.cpp No need to link the cython shared library, and it turns out that it is not a good idea to do so. 无需链接cython共享库,事实证明这样做并不是一个好主意。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM