项目中可能会经常用到第三方库,主要是出于程序效率考虑和节约开发时间避免重复造轮子。无论第三方库开源与否,编程语言是否与当前项目一致,我们最终的目的是在当前编程环境中调用库中的方法并得到结果或者借助库中的模块实现某种功能。这个过程会牵涉到很多东西,本篇文章将简要的介绍一下该过程的一些问题。
1.背景
多语言混合编程可以弥补某一种编程语言在性能表现或者是功能等方面的不足。虽然所有的高级语言都会最终转换成汇编指令或者最底层的机器指令,但是语言本身之间的千差万别很难一言以蔽之,这对不同语言之间相互通信造成很大的障碍。
工作中需要用python完成一项功能,但是所有现有的python库都不满足需求。最终找到了一个开源的C++库,编译得到动态库被python调用才完成工作需求。虽然整个过程耗时不多,但是期间碰到很多的问题,而且这些问题都很有思考价值。
除了这篇博文外,后续还将有一到两篇文章通过具体的实例讲解一下跨语言调用。
2.问题思考
在进行具体的介绍之前,先来思考一下调用外部库或者自己实现库所牵涉的一些一般性的问题。这样或许实际中操作使用时会理解的更加深刻,遇到问题也能够逐项的排查。
如果用C语言写的库调用了Linux的system call,纵使C本身是跨平台的,那么该库也不可能在Window上被使用,即便我们能拿到源码。这里有两个核心问题:
- 是否开源
- 是否跨平台
如果库的实现不依赖平台,且开源,那就意味着很大可能能在当前项目中使用。为什么是可能,因为即使库的实现语言和当前项目语言一致,也可能因为语言版本差异或者标准迭代导致不兼容。
最差的情况就是只能拿到编译后的库文件,且需在特定的平台运行。
作为库的开发者,最好是能够开源且库的实现不依赖于特定的平台,这样才能最大限度的被使用。
作为库的使用者,最不理想的情况是库可以在当前平台使用,但是只能拿到静态库或者动态库,且库的实现语言和当前项目语言不一致。
多数情况是第三方库是跨平台的且能够拿到源代码。这样的话如果两者的实现语言一致,我们可以直接将第三方库的代码移植到当前的项目中;如果实现语言不一致,需要在当前平台上将库的源码编译出当前平台上可用的库文件,然后在当前项目中引用编译生成的库文件。
本文将先简单的介绍在window平台上,使用python 2.7 自带的ctypes库引用标准的C动态库msvcrt.dll。这里可以先思考以下几个问题:
- python可不可以引用静态库?
- python中怎么拿到DLL导出的函数?
- python和C/C++之间的变量的类型怎样转换,如果是自定义的类型呢?
- 怎么处理函数调用约定(calling convention,eg:__cdecl,__stdcall,__thiscall,__fastcall)可能不同的问题?
- 如果调用DLL库的过程中出现问题,是我们调用的问题还是库本身的问题?应该怎样快速排查和定位问题?
- 有没有什么现有的框架能够帮我们处理python中引用第三方库的问题呢?
- 对于自定义的类型(class 和 struct)是否能在python中被引用。
关于函数调用约定,有必要简单的提一下:
Calling Convention和具体的编程语言无关,是由编译器、连接器和操作系统平台这些因素共同决定的。
The Visual C++ compilers allow you to specify conventions for passing arguments and return values between functions and callers. Not all conventions are available on all supported platforms, and some conventions use platform-specific implementations. In most cases, keywords or compiler switches that specify an unsupported convention on a particular platform are ignored, and the platform default convention is used.
这是MS的官方解释。注意最后一句话,表示对于函数调用,在平台不支持的情况下,语言中指定关键字或者编译器转换均可能无效。
接下的介绍中来我们将一一回答上面的问题。
2.导入C标准动态库
先来简单看一下python中如何引用C的标准动态库。
1 import ctypes, platform, time 2 if platform.system() == 'Windows': 3 libc = ctypes.cdll.LoadLibrary('msvcrt.dll') 4 elif platform.system() == 'Linux': 5 libc = ctypes.cdll.LoadLibrary('libc.so.6') 6 print libc 7 # Example 1 8 libc.printf('%s\n', 'lib c printf function') 9 libc.printf('%s\n', ctypes.c_char_p('lib c printf function with c_char_p')) 10 libc.printf('%ls\n', ctypes.c_wchar_p(u'lib c printf function with c_wchar_p')) 11 libc.printf('%d\n', 12) 12 libc.printf('%f\n', ctypes.c_double(1.2)) 13 # Example 2

