Python中Ctypes库与回调函数

原新浪博客(http://blog.sina.com.cn/billsona)搬迁至此。

近期项目组开发了一个dylib动态库,我主要负责对这个动态库进行测试,这期间碰到了一个回调函数的问题。说的直白一点,就是模拟上层应用调用dylib动态库的时候,dylib动态库还要回调上层应用提供的接口。
博主脑子转悠的不够快,绕来绕去,成功把我绕晕了。费了好长时间才理清楚其中的逻辑与关系。好记性不如烂笔头,赶紧记下来,防止后面又忘记了。
下面是用visio简单画的逻辑示意图,便于理解。
在这里插入图片描述
下面分步解析并实现一个回调函数的实例。
先用文本文件写一个简单的C程序,写完之后保存成libtest.c文件,代码如下。
在这里插入图片描述
程序解释:定义了一个名为max的void类型的函数,这个函数有3个参数,前面两个是int类型的参数,后面一个是一个返回值为int类型的函数的指针。在这个函数内部,处理也比较简单,实际上就是把前面两个参数传递给指针指向的函数去处理。从上面第三个参数也可以看出来,指针指向的函数处理完之后返回的是一个整形数据。
在mac上,通过命令行执行如下的命令,就可以把上面简单的C程序编译成dylib动态库。

gcc -shared -o libtest.dylib -fPIC libtest.c

这样就产生了一个简单的动态库文件,功能跟上面的C程序保持一致,不多解释。
下面来说说上述C程序中定义的函数中的第三个参数对应的函数。很显然,这个函数没有在上面的C程序中实现,我们在Python中实现,代码如下,很简单。

在这里插入图片描述
这几行代码太简单了,没啥好解释的,功能就是打印第一个参数a的类型,然后打印给定的两个数中较大的一个,最后返回这个较大的数。
到了这里,聪明的读者们可能已经猜到了,这个就是提供给动态库中的函数的第三个参数指向的函数函数吧,功能定义基本一样,给定两个int,返回一个int。
答案是否定的,我当时就是太“聪明”了,一直是这么认为了,在这个地方绕了半天没有绕出来。
下面通过两个问题来解释一下为什么这个函数不能直接给动态库中的函数当参数来调用。
首先,动态库是C程序,这个函数是Python程序,C程序能够直接调用Python程序吗?
其次,虽然这里不管是C还是Python都涉及到int型变量,C中的int和Python中的int是一样的吗?
很显然,这两个问题的答案都是否定的,C程序没法直接调用Python函数,C中的整型和Python中的整型虽然都标记为int,但是两者是不一样的。
这也是ctypes库存在的必要性。这个库为Python和C的混合编程提供了很多有用的方法。比如,Python调用动态库的方法,回调函数的方法,还有能够在Python和C上兼容的数据类型(上面提到了虽然C和Python中相同的数据类型都标记为int,但是实际上两者在机器中的形态可能是不一样的,ctypes库中的数据类型提供了这种兼容性,比如这里兼容int的c_int类型)。
在ctypes中,通过关键字CFUNCTYPE来声明一个回调函数的类型(通过关键字的字面意思也能够看出来,C-FUNC-TYPE,那就是C语言的函数(FUNC)类型(TYPE)嘛,理解了以后,感觉一切都很明了,之前不理解的时候,满脑子浆糊,这是些什么狗屁玩意儿…)。
我们可以用下面一行python语句来声明一个上述C程序中需要调用的回调函数的类型,并把这个回调函数类型实例化,回调函数类型实例就叫CMPFUNC。

CMPFUNC = CFUNCTYPE(c_int, c_int, c_int)

眼尖的同学可能已经发现了,上面不是求要获得两个数中最大的一个吗,这里声明回调函数类型的时候怎么变成了三个参数?其实吧,这里只是指的数据类型,并不是参数本身,后面两个c_int指的是传递给回调函数的参数类型,第一个c_int指的是回调函数的返回值类型。只有这样定义,C程序才能认识。
到了这里,核心内容基本就阐述完毕了,下面附上完整的Python程序代码及相关的注释说明和执行结果。
在这里插入图片描述
执行结果如下

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/lipeixinglive/article/details/113699571