Swift51.com
麦子学院 头像
麦子学院  2017-07-17 17:05

python和C的互相调用方法步骤详解

回复:0  查看:2733  
本文和大家分享的是python c 的互相调用相关知识,一起来看看吧,希望对大家 学习python有所帮助。
  最近在考虑基于udp 做一个用于网游战斗中的数据同步协议,为了前期测试数据,决定先做一个外部的代理 tunnel ,原理是在 server 端和 client 端分别建立网络转发 proxy ,即原来的 C/S 连接改为两个 proxy 之间数据快速传输。因为 udp 库是用 C++ 写的代码,在测试数据的时候需要不断地修改参数,重新编译,修改输出统计数据制表等,不胜其烦,最终决定导出接口由 python 脚本来进行逻辑调用。
  C/C++ 导出到 python 有多种方法,根据不同的需求,可以使用下面不同的方式:
  1.ctypes 绑定。 ctypes 就包含在万能的 python 标准库模块里面,它可以运行时载入动态链接库( dll so ),在 CPython 2.x/3.x PyPy 上都支持。这种方式好处就是不用针对性地用 python api 写导出函数,可以直接加载动态链接库的符号表,在 python 中就可以直接调用了。
  2. 第三方的 python binding 。例子有 boost-python ,实现方式是工具自动化用 Python/C api 生成一系列 C++ wrapper 函数。特别适用于大型的库或引擎导出到 python
  3. 手动写 python binding 函数。如果对 Python C api 熟悉的话,这种方式应该是最灵活的,读一遍 API 文档就可以使用。理论上效率应该是最好的,但对于我这种 python 初学者,可能需要花上不少时间。
  以之前折腾C 函数导出到 Lua 脚本的经历,本以为要先研究一番 python c api ,再搞上半天才能搞定。后面发现 python 标准库模块的 ctypes 已经非常强大,虽然性能应该是三种方式里面最差的,但在这个最高 60fps tunnel 里面, C/Python 接口边界调用的损耗先忽略。跟其他两种方式设计不一样的是, ctypes 采用的是非入侵式调用接口的方式,不需要修改原来的 C 接口或者写一些绑定代码,直接对编译出来的动态库进行调用。 ctypes 使用过程也是非常愉悦的。
  下面介绍下ctypes 的使用:
  1.  加载 DLL 动态链接库
  这里需要注意区分动态链接库函数是使用cdecl 还是 stdcall 的调用约定,分别使用 cdll windll 加载动态库。
  例如:
  加载 udp 库函数  udp_server = cdll.LoadLibrary("./udp_server.so")  init_udp_server = udp_server.init_udp_server  destroy_udp_server = udp_server.destroy_udp_server  update_udp_server = udp_server.update_udp_server  SendMsg = udp_server.SendMsg
  SetConnectCallback = udp_server.SetConnectCallback  SetDisconnectCallback = udp_server.SetDisconnectCallback  SetTimeoutCallback = udp_server.SetTimeoutCallback  SetRecvCallback = udp_server.SetRecvCallback
  2.  数据类型映射
  除了ctypes 定义的基本数据类型( c_char, c_int, c_double 等),还能使用 pointer 函数转换成指针类型。对于要导出的网络库,设置回调函数是必不可少的,在 C++ 库里面,回调函数是通过设置一个函数指针完成的, ctypes 同样支持函数指针的声明。如: recv_cb = CFUNCTYPE( None, c_char_p, c_int ) ,表示一个返回值为 void ,参数为 char* int 类型的回调函数。
   def  __init__( self, port, ip="127.0.0.1"):
   self._port = port
   self._ip = ip
   self._clients = {}
   self.c_connect_cb = connect_cb( self.server_connect)
   self.c_disconnect_cb = disconnect_cb( self.server_disconnect)
   self.c_timeout_cb = timeout_cb( self.server_timeout)
   self.c_recv_cb = recv_cb( self.server_recv)
   def  create( self):
   if  self._port:
   if init_udp_server( self._ip,  self._port) == 0:
  print "server listen %s:%d" % ( self._ip,  self._port)
  SetConnectCallback(  self.c_connect_cb )
  SetDisconnectCallback(  self.c_disconnect_cb )
  SetTimeoutCallback(  self.c_timeout_cb )
  SetRecvCallback(  self.c_recv_cb )
   return True
  print "[error] init_udp_server error",  self._ip,  self._port
   return False
  绑定回调参数需要注意的是,绑定的回调函数需要保存为成员变量(上面的写法),目的是避免python 垃圾回收导致回调函数变成野指针。这算是一个小小的坑吧。基本上一个小小的库也就用到这些功能。
来源:简书