前言
python拥有非常强大的生态,而且安装使用极其方便,除了效率不高以外其他基本上没有缺点。C/C++在开发、编译、使用方面有点麻烦,但是运行效率极其高。
最近准备把工具类的软件用python来开发,python没有的库就用C/C++来开发,并让python去调用。
这里发现python内置了C调用的方法,通过Ctypes模型进行类型映射之后,即可直接调用C库。
Ctypes
ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模块以纯 Python 形式对这些库进行封装。使用参考:
https://docs.python.org/zh-cn/3.7/library/ctypes.html
这里最核心的其实就是类型映射,需要将python的类型转成C的数据类型,具体类型如下图:
示例一:HelloWorld
写一个C函数,输出helloworld,然后通过python来调用。
C函数如下:
extern "C" __declspec(dllexport) void say_hello()
{
std::cout << "Hello, from MyCType!\n"
<< std::endl;
}
python调用如下:
import ctypes
libc = ctypes.cdll.LoadLibrary(
r'D:\work\code\study\github\other\python\ctypes\cplus\build\Debug\MyCType.dll')
libc.say_hello()
说明:
1、这里通过ctypes.cdll.LoadLibrary来加载动态库
2、之后就可以直接调用函数
示例二:字符串加法
C函数如下:
extern "C" __declspec(dllexport) const char *say_add(const char *a, const char *b)
{
std::string aa = a;
std::string bb = b;
std::string cc = aa + bb;
std::cout << aa << std::endl;
std::cout << bb << std::endl;
std::cout << cc << std::endl;
char *dd = new char(int(cc.length()) + 1);
memset(dd, 0, cc.length() + 1);
memcpy(dd, cc.c_str(), cc.length());
return dd;
}
python调用
string1 = "my string 1"
string2 = "my string 2"
b_string1 = string1.encode('utf-8')
b_string2 = string2.encode('utf-8')
libc.say_add.restype = ctypes.c_char_p
c = libc.say_add(ctypes.c_char_p(b_string1),
ctypes.c_char_p(b_string2))
print(type(c))
print(c.decode('utf-8'))
说明:
1、需要先将python字符串转成字节流,然后再强制转为ctypes.c_char_p类型
2、需要手动设置函数返回类型,通过libc.say_add.restype
3、将c_char_p转成字符串通过c.decode('utf-8')来实现
示例三:结构体映射
C定义如下:
struct MyCType
{
char *aa;
int b;
};
extern "C" __declspec(dllexport) const int say_struct(MyCType &mycType)
{
std::cout << mycType.aa << std::endl;
std::cout << mycType.b << std::endl;
return 0;
}
python调用:
class MyCType(ctypes.Structure):
_fields_ = [("aa", ctypes.c_char_p), ("b", ctypes.c_int)]
testStruct = MyCType()
testStruct.aa = string1.encode('utf-8')
testStruct.b = 250
libc.say_struct(testStruct)
说明:
1、结构体传参需要首先在python里面定义一个结构体对象,并与C结构体做好映射,之后的调用流程和前面一致。
示例四:回调函数
还可以在C中调用python的函数
C定义如下:
typedef void(__stdcall *callBackTest)(int a, int b);
extern "C" __declspec(dllexport) const int say_callback(callBackTest back)
{
back(1, 2);
return 0;
}
python调用如下:
def callbackFun(a, b):
print(a)
print(b)
return 0
testFunc = ctypes.WINFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_int)
back = testFunc(callbackFun)
libc.say_callback(back)
说明:
1、WINFUNCTYPE这个函数设置了3个参数,实际回调函数只有2个参数,原因是第一个参数表示函数返回值。
总结
本文主要介绍了如何通过python的内置模块Ctypes来调用C库。并列举了HelloWorld、字符串加法、结构体传参、回调函数四个典型的例子。
获取工程途径:
关注公众号,加技术交流群即可获取本工程实例代码。