混合编程—C++程序中python脚本的嵌入方法(理论部分)

一、C++与Python高级编程语言简概

(一)C++

        C++是一种被广泛使用的计算机程序设计语言。它是一种通用程序设计语言,支持多重编程范式,例如过程化程序设计(Procedural programming)、面向对象程序设计(object-oriented programming)泛型程序设计(generic programming)和函数式程序设计(Functional programming)等。作为广泛被使用的工业语言,C++存在多个流行的成熟实现:GCC、基于LLVM的Clang以及Visual C++等。这些实现同时也是成熟的C语言实现,但对C语言的支持程度不一。大多数流行的实现包含了编译器和C++部分标准库的实现。编译器直接提供核心语言规则的实现,而库提供ISO C++标准库的实现[4]。

         基于C++的面向对象程序设计的基本思想及主要特点为:抽象封装继承多态。C++程序的开发通常要经过编辑编译链接运行调试。其中,链接是将多个目标文件以及所需的库文件链接成最终的可执行文件(executable file,exe)的过程,包括合并段、调整段偏移、汇总所有符号以及完成所有符号的重定位/*6*/。具体流程如下所示:

图源:C++语言程序设计 / 郑莉,董渊编著.—5版.—北京:清华大学出版社

C++的适用时机/*4*//*1*/:

        Ⅰ C++适合构造程序中需求稳定的部分,需求变化较大的部分可使用脚本语言;

        Ⅱ 程序需尽量发挥硬件的最高性能,且性能瓶颈在于CPU和内存;

        Ⅲ 程序需频繁地与操作系统或硬件沟通;

        Ⅳ 程序必须使用C++框架/库,如大部分游戏引擎(如Unreal/Source)及中间件(如Havok/FMOD),虽然有些C++库提供其他语言的绑定,但通常原生的API性能最好、最新;

        Ⅴ 项目中某个目标平台只提供C++编译器的支持。         

        按应用领域来说,C++适用于开发服务器软件、桌面应用、游戏、实时系统、高性能计算、嵌入式系统等。

C++性能优于其他语言的原因/*4*/:

        Ⅰ C/C++直接以静态形式把源程序编译为目标平台的机器码;

        Ⅱ 一般而言,C/C++程序在编译及链接时可进行的优化最丰富,启动时的速度最快,运行时的额外内存开销最少,而C/C++相对于动态脚本语言(如Python)减少了运行时的动态类型检测;

        Ⅲ C/C++的运行行为是确定的,且不会有额外行为,也不会有如垃圾收集/*12*/而造成的不确定性延迟,再者,C/C++的数据结构在内存中的布局也是确定的;

        Ⅳ C++的一些功能会使程序性能优于C,其中以内联和模板最为突出。再者,C/C++能直接映射机器码,之间没有另一层中间语言,因此可以做底层优化。

        总而言之,C++许多的性能优点也会带来部分代价,如较长的编译/链接时间、容易出错等导致开发时间和成本增加。

(二)Python

        Python,是一种广泛使用的解释型、高级和通用的编程语言(脚本语言)。Python支持多种编程范型,包括结构化、过程式、反射式、面向对象和函数式编程。它拥有动态类型系统和垃圾回收(引用计数和环检测相结合)功能,能够自动管理内存使用,并且其本身拥有一个巨大而广泛的标准库以及大量的第三方库。Python的设计哲学,强调代码的可读性和简洁的语法,尤其是使用空格缩进来划分代码块。相比于C/C++,Python能让开发者使用更少的代码表达想法[5]。

Python的主要特点/*11*/:

        Ⅰ 跨平台:Python支持Windows、Linux和MAC os等主流操作系统;

        Ⅱ 可移植:代码通常不需要多少改动就能移植到别的平台上使用;

        Ⅲ 可扩展:Python语言本身由C语言编写而成的,既可以在Python中嵌入C语言以提高代码的运行速度和效率,也可以使用C语言重写Python的任何模块;

        Ⅳ 交互式:Python提供很好的人机交互界面,如IDLE和IPython。可以从终端输入执行代码并获得结果,互动的测试和调试代码片段;

        Ⅴ 解释型:Python语言在执行过程中由解释器逐行分析翻译,逐行运行并输出结果;

        Ⅵ 面向对象:Python语言具备所有面向对象特性和功能,支持基于类的程序开发;

        Ⅶ 动态语言:在运行时可以改变其结构。例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其它结构上的变化。

        值得注意的是,Python遵循GPL开源协议,Python编写的源代码通常是不加密的,即发布Python程序,实际上就是发布源代码。对于代码封闭而言,编译型语言(如C++,非ISO)只需发布可执行文件即可,而解释型语言(如Python)则必须发布源码,但是为了增加反编译的门槛和难度,可以使用类似py2exe工具将python源码转换成一个类似于exe的可执行文件形式。

Python的常见应用方向/*11*/:

        常规软件开发、科学计算、自动化运维、云计算、WEB开发、网络爬虫、数据分析、人工智能。

二、基础概念        

        回调函数/*8*//*9*/:在计算机程序设计中,回调函数,简称回调(Callback),是指通过参数将函数传递到其他代码的,某一块可执行代码的引用。回调的形式因程序设计语言有差别,如C++通过将函数指针作为参数传递给其他函数。使用回调的例子有QT中信号槽机制等。

        封装:封装是面向对象方法的一个重要原则,就是把对象的属性和服务结合成一个独立的系统单位(降低耦合),并尽可能隐蔽对象的内部细节。对于“信息隐蔽”,我们可以封装成类,只保留有限的对外接口使之与外部发生联系,从而对外形成一个边界,尽可能屏蔽对象的内部细节。

        脚本程序(Script):使用脚本语言所写的程序。脚本语言是为了缩短传统的“编写、编译、链接、运行”过程而创建的计算机编程语言。早期的脚本语言经常被称为批处理语言或作业控制语言。大多数脚本语言的共性是:良好的快速开发,有效率的执行、解释而非编译执行、和其它语言编写的程序组件之间通信功能强大,但是脚本缺少优化程序以提速或者降低内存的伸缩性。脚本通常以文本(如ASCII)保存,只在被调用时进行解释。

        extern(c++关键字)/*10*/:

        ①修饰全局变量:在声明全局变量的时候使用extern修饰变量,表明该变量定义于其他翻译单元。②修饰全局常量:表明该全局常量拥有外部链接(可以被其他翻译单元发现),否则全局常量默认是只有内部链接,即不可被其他翻译单元发现。③修饰局部变量:表明该局部变量在其他翻译单元中被定义,需要在链接的时候去解析。④修饰一个字符串:形如extern “C” 之类的用法表明后接的代码块(或者后接的声明)使用C语言调用惯例。⑤修饰一个模板:表明该模板已经在其他翻译单元实例化,不需要在这里实例化。

        (注:ISO C和ISO C++从未明确要求源程序被编译,而仅要求翻译,因此C和C++并不是所谓的完全编译型语言。技术上,实现C和C++程序的单位是翻译单元(translation unit),但是C/C++在传统上多以编译器实现[4]。)

/**/ 程序开发基本术语[6]

        源程序:用源语言编写的、有待翻译的程序。源语言可以是汇编语言,也可以是高级程序设计语言,用它们写出的程序都是源程序;

        目标程序:是源程序通过翻译加工以后所生成的程序。目标程序可以用机器语言表示,也可以用汇编语言或其它中间语言表示;

        翻译程序:是指用来把源程序翻译为目标程序的程序。对于翻译程序来说,源程序是它的输入,而目标程序则是其输出。翻译程序分为汇编程序编译程序以及解释程序三种类型,①汇编程序的任务是把用汇编语言写成的源程序翻译成机器语言形式的目标程序;②编译程序是指高级程序设计语言所写的源程序经翻译程序一次性加工生成目标程序(机器码)的过程;③解释程序同样是将高级语言源程序翻译为机器指令的过程。但不同于编译程序的是,它是边翻译边执行的,即输入一句,翻译一句,执行一句,直至将整个源程序翻译并执行完毕。

        编译型语言与解释型语言的区别/*3*/:        

        编译型语言(如C++等,非ISO),其需要针对不同操作系统进行编译以生成相应的底层机器码、若修改源文件则需要重新编译再执行。优点为运行速度快,缺点为繁琐、调试额外耗时和跨平台性较差;解释型语言(如Python等),其基于解释器动态将源代码边解释为机器码,边执行机器命令。优点为跨平台性好、调试较为迅速,缺点为依赖解释器、运行效率较低。

三、混合编程(C++/python)基本知识与封装规范[1][2]

(一)Python扩展(Extending Python)

        目的:附加C函数库来扩展Python的功能。

        应用程序主程序:Python解释器

        流程:首先,将数据值从Python类型转换为C类型。然后,使用转换后的值对C例程执行函数调用。最后,将调用返回的结果数据值从C类型转换为Python类型。

(二)Python嵌入(Embedding Python)

        目的:嵌入Python丰富C/C++应用程序,通常嵌入功能利用Python编写更为简单、逻辑清晰且独特可定制。

        应用程序主程序:C++编译器(需要的时候动态调用Python解释器)

        流程:首先,将数据值从C类型转换为Python类型。然后,使用转换后的值对Python接口例程执行函数调用。最后,将调用返回的结果数据值从Python类型转换为C类型。

(三)C++和Python混合编程的优势与使用Python C API的注意事项[12]

        优势

        Ⅰ 主体系统使用C++实现,保持系统的高效;

        Ⅱ 控制部分使用Python,增加开发效率,Python的内存垃圾回收、丰富的类库都使C++开发者获益匪浅;

        Ⅲ Python脚本可以运行期重载,可以实现控制部分不停机热更新。

        使用Python C API的注意事项

        Ⅰ Python主要使用引用计数管理内存,调用Python C API时对于返回值返回的是借用的引用还是新的引用,需要根据文档仔细确认。否则,轻则出现内存泄漏,重则程序崩溃;

        Ⅱ Python中的数据结构与C++的有很大不同。Python常用的有tuple、list、dict,而C++常用的是vector、list、map,并且C++是强类型的。当C++与Python进行交互时,C++层希望操作Python数据结构就像操作C++ STL一样方便,而在Python脚本层,又希望C++传入的参数或返回值都是原生的Python数据;

        Ⅲ C++中常用指针传递对象,当嵌入Python时,需要把C++对象传递到Python中。

不同文件扩展名文件认识

.h                --- C++头文件

.cpp            --- C++源程序文件

.obj             --- Windows系统下的C++源程序经过编译后生成的目标文件(UNIX系统为.o)

.exe            --- Windows系统下的可执行程序(多个目标文件与库/资源文件的链接生成)

.dll   /*13*/  --- Windows系统下的动态链接库(Dynamic Link Library),.dll文件有如下用途:

                        ①代码共享:.dll文件中一些功能或服务的实现代码,多个应用程序可以共享同一个.dll文件,避免重复编写相同的代码;

                        ②动态链接:.dll文件是在运行时动态链接到应用程序中的;

                        ③扩展性:通过使用.dll文件,可以方便地向现有的应用程序添加新的功能或模块,而不需要修改或重新编译整个应用程序。只需将新的.dll文件提供给应用程序,然后在运行时加载和使用新的功能;

                        ④动态更新:由于.dll文件是独立于应用程序的,可以对.dll文件进行更新和升级,而不影响应用程序的正常运行。这使得软件的维护和升级变得更加灵活和方便。

.lib    /*14*/    --- Windows系统下的完全静态链接库(包含所有的导出声明和实现)或伴随.dll生成的动态.lib(动态.lib只包含一些函数索引信息,记录了.dll中那些函数的入口和位置)

.py              --- Python源程序文件

.pyd            --- 一般是C/C++编写的Python扩展模块,即Python的一个动态链接库(Linux 系统中一般为.so文件)

注意事项

       Ⅰ 完全静态.lib文件实际上就是任意个.obj文件的集合(任意个.obj文件链接生成一个完全静态.lib);

        Ⅱ .dll近似于.exe,只不过没有主函数(main)而不能单独执行。实际工程中,很多应用程序都并不是一个完整的单独可执行文件,它们被分割成一些单独的相对独立的动态链接库,只有在执行应用程序的时候,用到的.dll才会被调用。

参考资料:

官方文档

[1] Extending and Embedding the Python Interpreter — Python 3.9.17 documentation

[2] Python/C API Reference Manual — Python 3.9.17 documentation

[3] Welcome to Cython’s Documentation — Cython 3.0.7 documentation

Wikipedia

[4] https://zh.wikipedia.org/wiki/C%2B%2B(C++)

[5] https://zh.wikipedia.org/wiki/Python(Python)

参考书籍

[6] C++语言程序设计 / 郑莉,董渊编著.—5版.—北京:清华大学出版社,2020.11.

优秀博客及学习视频资源

[7] c++ 调用 python_哔哩哔哩_bilibili

[8] C++调用Python(混合编程)函数整理总结-CSDN博客

[9] Python与C/C++混合编程 - 知乎

[10] C++(Qt)与Python混合编程(一) - 知乎

[11] C++(Qt)与Python混合编程(二) - 知乎

[12] C++使用ffpython嵌入和扩展python-阿里云开发者社区

[13] Docs(C++调用Python脚本)

拓展资料

/*1*/ 站在巨人的肩膀上,C++开源库大全-阿里云开发者社区

/*2*/ github上总结的python资源列表【转】-阿里云开发者社区

/*3*/ 计算机基础------计算机语言分类(脚本语言引发的思考)-阿里云开发者社区

/*4*/ C++强大的背后-阿里云开发者社区

/*5*/ 你知道.c是如何变成.exe的吗 - 知乎

/*6*/ C/C++编译链接 - 知乎

/*7*/ Python PyInstaller 打包成 Win、Mac 应用程序(app / exe)-阿里云开发者社区

/*8*/ 回调函数详解-CSDN博客

/*9*/ https://zh.wikipedia.org/wiki/%E5%9B%9E%E8%B0%83%E5%87%BD%E6%95%B0(回调函数)

/*10*/ C++中的extern_c++ extern-CSDN博客

/*11*/ 1-Python介绍-阿里云开发者社区

/*12*/  C/C++中几种经典的垃圾回收算法_c 垃圾回收算法-CSDN博客

/*13*/ c++中什么叫dll文件,有什么用_c++推送数据dll文件-CSDN博客

/*14*/ 关于lib文件的介绍-CSDN博客

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在C宿主程序设计钩子,通过钩子使用Python编写业务逻辑可以采取以下步骤: 1. 确定钩子的触发时机和调用方式。 2. 安装Python解释器,并在C程序嵌入Python解释器。 3. 编写Python脚本实现业务逻辑,可用Python的扩展库实现业务逻辑的功能。 4. 在C程序调用Python解释器并执行相应的Python脚本。具体实现方式可以采用Python/C API来实现。 下面是一个示例程序,其C程序通过调用Python钩子执行Python脚本,实现了简单的业务逻辑。 ```c #include <Python.h> /* 定义Python脚本路径 */ #define PY_SCRIPT_PATH "/path/to/your/python/script.py" /* 定义Python钩子函数,用于在C程序触发Python脚本 */ void run_python_hook() { PyObject *pName, *pModule, *pFunc; Py_Initialize(); /* 加载Python脚本 */ pName = PyUnicode_DecodeFSDefault(PY_SCRIPT_PATH); pModule = PyImport_Import(pName); Py_DECREF(pName); if (pModule != NULL) { /* 获得Python函数指针 */ pFunc = PyObject_GetAttrString(pModule, "my_func"); if (pFunc && PyCallable_Check(pFunc)) { /* 执行Python函数 */ PyObject_CallObject(pFunc, NULL); } else { printf("Python function is not callable\n"); } Py_XDECREF(pFunc); Py_DECREF(pModule); } else { PyErr_Print(); printf("Failed to load Python script\n"); } Py_Finalize(); } /* 在C程序调用Python钩子 */ int main(int argc, char *argv[]) { // ... /* 在需要调用Python脚本的位置调用钩子函数 */ run_python_hook(); // ... return 0; } ``` 在上面的示例程序,`run_python_hook`函数使用Python/C API加载并执行指定的Python脚本,并调用脚本的`my_func`函数实现业务逻辑。C程序的`main`函数通过调用`run_python_hook`函数来触发Python脚本的执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

doll ~CJ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值