python与C库交互

f2b68c3633833640a0d5325e069a24d5.png

前言


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的数据类型,具体类型如下图:

f511de4ca940b4226c6972a9ebf6d4bb.png

示例一: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、字符串加法、结构体传参、回调函数四个典型的例子。

获取工程途径:

关注公众号,加技术交流群即可获取本工程实例代码。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值