如何在C++中调用经Cython编译为C的Python函数?
问题
通常开发者会用C扩展Python,但我需要相反的场景:在C++项目中使用Python代码。
我已通过Cython将简单的.pyx文件转换为C文件,函数仅包含打印逻辑,代码如下:
# hello_cython.pyx def func1(): print("Hello Cython Function.") if __name__ == "__main__": print("Hello World! Run cython.")
参考用的setup.py代码:
# setup.py from setuptools import setup, Extension from Cython.Build import cythonize setup( name="AppHi", ext_modules=cythonize("hello_cython.pyx", show_all_warnings=True), )
执行python setup.py build_ext --inplace生成C文件后,我将其加入C项目,已修复缺失的头文件和库依赖,编译无报错,但C的main函数中无法调用原Python定义的func1:
// LearnPythonAPIIntegration.cpp : This file contains the 'main' function. Program execution begins and ends there. // #include <iostream> int main() { std::cout << "Hello World!\n"; /// 想在这里调用func1() }
不清楚是否操作有误,或是无法复用该代码只能用C++重写,请问如何解决?
解决方案
1. 修改Cython代码暴露C可调用接口
默认Cython生成的函数仅作为Python模块内部接口,无法直接被C/C++调用,需要显式声明C兼容的接口。修改hello_cython.pyx:
# hello_cython.pyx cdef public void func1_c(): print("Hello Cython Function.") def func1(): func1_c() if __name__ == "__main__": print("Hello World! Run cython.")
这里用cdef public声明一个C可见的函数func1_c,原Python函数func1可保留供Python调用,同时C/C++能直接调用func1_c。
2. 重新生成编译文件
执行原命令重新生成C文件和对应头文件:
python setup.py build_ext --inplace
生成的文件中会包含hello_cython.h,里面有func1_c的函数原型声明。
3. 在C++项目中调用函数
3.1 引入头文件
在C++代码中添加必要的头文件:
#include <iostream> #include "Python.h" #include "hello_cython.h"
3.2 初始化Python运行时并调用函数
由于Cython生成的代码依赖Python解释器,必须先初始化再调用函数:
int main() { std::cout << "Hello World!\n"; // 初始化Python解释器 Py_Initialize(); // 导入生成的模块(必须执行,否则函数无法正常工作) PyImport_ImportModule("hello_cython"); // 调用Cython暴露的C函数 func1_c(); // 清理Python解释器 Py_Finalize(); return 0; }
3.3 编译链接配置
确保C++项目的编译配置包含:
- Python头文件目录(如Linux下
/usr/include/python3.x,Windows下Python安装目录的include文件夹) - 链接Python库文件(如Linux下
python3.x,Windows下python3x.lib) - 链接Cython生成的目标文件或库文件(如
hello_cython.o或hello_cython.lib)
替代方案:直接通过Python C API调用
如果不想修改Cython代码,也可以直接用Python C API在C++中调用Python模块的函数:
#include <iostream> #include "Python.h" int main() { std::cout << "Hello World!\n"; Py_Initialize(); // 导入目标模块 PyObject* module = PyImport_ImportModule("hello_cython"); if (!module) { PyErr_Print(); return 1; } // 获取函数对象 PyObject* func = PyObject_GetAttrString(module, "func1"); if (!func || !PyCallable_Check(func)) { PyErr_Print(); Py_DECREF(module); return 1; } // 调用无参函数 PyObject* result = PyObject_CallObject(func, nullptr); if (!result) { PyErr_Print(); } // 释放Python对象引用 Py_XDECREF(result); Py_DECREF(func); Py_DECREF(module); Py_Finalize(); return 0; }
这种方式无需修改Cython代码,但需要手动管理Python对象的引用计数。
内容的提问来源于stack exchange,提问作者Grzegorz Krug
相关产品推荐
相关产品推荐

