是否可以构建具有某些cdef功能的 cython 模块并将生成的共享库链接到 C++ 程序中?
我尝试了一个概念证明:
cymod.pyx:
# distutils: language=c++
from libcpp.string cimport string
cdef public string simple_echo(string test_string):
return test_string
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
设置.py:
from setuptools import setup, Extension
from Cython.Build import cythonize
setup(
name='cymod',
ext_modules=cythonize(
Extension(
"cymod", ["cymod.pyx"],
),
)
)
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
这很奇怪。生成的头文件有:
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 */
Run Code Online (Sandbox Code Playgroud)
我在以下位置看到我的功能cymod.so:
nm cymod.so| grep simple_echo
0000000000001e50 T simple_echo
Run Code Online (Sandbox Code Playgroud)
注意:我意识到,要真正实现此功能,我需要链接到 python 库并初始化解释器等。我将其省略,以使其更短,但无论哪种方式,我都会遇到相同的错误。
简短的回答是我-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"
Run Code Online (Sandbox Code Playgroud)
我还将 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;
}
Run Code Online (Sandbox Code Playgroud)
注意:调用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以及他对相关问题/sf/answers/3179730431/
在阅读链接的文档时,我发现了这一点:
注意 在 Linux 等某些操作系统上,还可以首先以通常的方式构建 Cython 扩展,然后像动态库一样链接到生成的 .so 文件。请注意,这是不可移植的,因此应该避免。
所以事实证明你不应该做我想做的事。
相反,我应该做的是运行:
cython --cplus cymod.pyx
Run Code Online (Sandbox Code Playgroud)
然后cpp_test.cpp用生成的cymod.cpp文件进行编译。不需要链接 cython 共享库,事实证明这样做并不是一个好主意。