CFFI可以在四种模式中使用:“ABI”和“API”级别,每种模式都有 in-line 或 out- line 准备(或编译)

ABI模式从二进制级别访问库,而更快的API模式通过C编译器访问库

在 in-line 模式中,每次导入Python代码时都会设置所有内容

在 out- line 模式中,有一个单独的准备步骤(可能还有C编译),它生成一个模块,主程序可以导入该模块

| 版权声明:itisyang,未经博主允许不得转载。

简单例子(ABI, in-line)

>>> from cffi import FFI
>>> ffi = FFI()
>>> ffi.cdef("""
...     int printf(const char *format, ...);   // copy-pasted from the man page
... """)
>>> C = ffi.dlopen(None)                     # loads the entire C namespace
>>> arg = ffi.new("char[]", "world")         # equivalent to C code: char arg[] = "world";
>>> C.printf("hi there, %s.\n", arg)         # call printf
hi there, world.
17                                           # this is the return value
>>>
复制代码

注意,在Python 3中,需要将byte strings传递给char *参数,在上面的示例中,它将是b“world”和b“hi there, %s!\n”,通常也可以这样 somestring.encode(myencoding)

Windows上的Python 3: ffi.dlopen(None)无法工作。这个问题很混乱,而且无法真正解决。如果尝试从系统上存在的特定DLL调用函数,则不会出现问题: 使用ffi.dlopen(“path.dll”)。

这个例子不调用任何C编译器。它在所谓的ABI模式下工作,这意味着如果您调用某个函数或访问某个在cdef()中稍有错误声明的结构的某些字段,它就会崩溃。

如果使用C编译器安装模块是一个选项,那么强烈建议使用API模式。(它也更快。)

结构/数组的例子(in-line)

from cffi import FFI
ffi = FFI()
ffi.cdef("""typedef struct {unsigned char r, g, b;} pixel_t;
""")
image = ffi.new("pixel_t[]", 800*600)f = open('data', 'rb')     # binary mode -- important
f.readinto(ffi.buffer(image))
f.close()image[100].r = 255
image[100].g = 192
image[100].b = 128f = open('data', 'wb')
f.write(ffi.buffer(image))
f.close()
复制代码

您可以调用ffi.new(“pixel_t[600][800]”)获得一个二维数组。

API模式,调用C标准库

# file "example_build.py"# Note: we instantiate the same 'cffi.FFI' class as in the previous
# example, but call the result 'ffibuilder' now instead of 'ffi';
# this is to avoid confusion with the other 'ffi' object you get belowfrom cffi import FFI
ffibuilder = FFI()ffibuilder.set_source("_example",r""" // passed to the real C compiler,// contains implementation of things declared in cdef()#include <sys/types.h>#include <pwd.h>// We can also define custom wrappers or other functions// here (this is an example only):static struct passwd *get_pw_for_root(void) {return getpwuid(0);}""",libraries=[])   # or a list of libraries to link with# (more arguments like setup.py's Extension class:# include_dirs=[..], extra_objects=[..], and so on)ffibuilder.cdef("""// declarations that are shared between Python and Cstruct passwd {char *pw_name;...;     // literally dot-dot-dot};struct passwd *getpwuid(int uid);     // defined in <pwd.h>struct passwd *get_pw_for_root(void); // defined in set_source()
""")if __name__ == "__main__":ffibuilder.compile(verbose=True)
复制代码

您需要运行example_build.py脚本一次生成“source code”到文件_example.c中。并将其编译为一个常规的c扩展模块。(CFFI根据set_source()的第二个参数是否为None,选择生成Python或C模块。)

这一步需要一个C编译器。它生成一个名为 _example.so 或 _example.pyd 的文件。如果需要,它可以像其他扩展模块一样以预编译的形式发布。

在主程序中使用:

from _example import ffi, libp = lib.getpwuid(0)
assert ffi.string(p.pw_name) == b'root'
p = lib.get_pw_for_root()
assert ffi.string(p.pw_name) == b'root'
复制代码

注意,passwd 结构体是独立精确的C布局结构(它是“API级别”,而不是“ABI级别”),需要一个C编译器才能运行 example_build.py 。

还要注意,在运行时,API模式比ABI模式快。

把这个模块使用Setuptools集成到setup.py中:

from setuptools import setupsetup(...setup_requires=["cffi>=1.0.0"],cffi_modules=["example_build.py:ffibuilder"],install_requires=["cffi>=1.0.0"],
)
复制代码

API模式,调用C源代码而不是编译库

如果您想调用一些没有预编译的库,但是有C源代码的库,那么最简单的解决方案是创建一个单独的扩展模块,该模块由这个库的C源代码和额外的CFFI包装器编译而成。例如,假设您从文件 pi.c 和 pi.h 开始:

/* filename: pi.c*/
# include <stdlib.h>
# include <math.h>/* Returns a very crude approximation of Pigiven a int: a number of iteration */
float pi_approx(int n){double i,x,y,sum=0;for(i=0;i<n;i++){x=rand();y=rand();if (sqrt(x*x+y*y) < sqrt((double)RAND_MAX*RAND_MAX))sum++; }return 4*(float)sum/(float)n; }
复制代码
/* filename: pi.h*/
float pi_approx(int n);
复制代码

创建一个名为 pi_extension_build.py 的脚本,构建C扩展:

from cffi import FFI
ffibuilder = FFI()ffibuilder.cdef("float pi_approx(int n);")ffibuilder.set_source("_pi",  # name of the output C extension
"""#include "pi.h"',
""",sources=['pi.c'],   # includes pi.c as additional sourceslibraries=['m'])    # on Unix, link with the math libraryif __name__ == "__main__":ffibuilder.compile(verbose=True)
复制代码

构建扩展:

python pi_extension_build.py
复制代码

可以发现,在工作目录中,生成的输出文件: _pi.c, _pi.o 和编译后的C扩展(例如Linux上生成 _pi.so )。它可以从Python调用:

from _pi.lib import pi_approxapprox = pi_approx(10)
assert str(pi_approximation).startswith("3.")approx = pi_approx(10000)
assert str(approx).startswith("3.1")
复制代码

Out-of-line, ABI level

out-of-line ABI 模式是 常规(API)out-of-line模式和in-line ABI 的混合,优点是不需要C编译器),缺点是更容易崩溃。

这种混合模式可以大大减少导入时间,因为解析大型C头文件很慢。它还允许您在构建时进行更详细的检查,而不必担心性能。

# file "simple_example_build.py"from cffi import FFIffibuilder = FFI()
ffibuilder.set_source("_simple_example", None)
ffibuilder.cdef("""int printf(const char *format, ...);
""")if __name__ == "__main__":ffibuilder.compile(verbose=True)
复制代码

运行一次会产生_simple_example.py,你的主程序只导入生成的模块,而不再需要 simple_example_build.py

from _simple_example import ffilib = ffi.dlopen(None)      # Unix: open the standard C library
#import ctypes.util         # or, try this on Windows:
#lib = ffi.dlopen(ctypes.util.find_library("c"))lib.printf(b"hi there, number %d\n", ffi.cast("int", 2))
复制代码

注意,ffi.dlopen()不会调用任何额外的方法来定位库,这意味着 ffi.dlopen(“libfoo.so”)是可以的,但是ffi.dlopen(“foo”)不行。 在后一种情况下,您可以将其替换为ffi.dlopen(ctypes.util.find_library(“foo”))。并且,None 只能在Unix打开C标准库。

为了便于分发,你可以把它静态地包含在你的项目的源文件中,使用Setuptools在setup.py这样编写:

from setuptools import setupsetup(...setup_requires=["cffi>=1.0.0"],cffi_modules=["simple_example_build.py:ffibuilder"],install_requires=["cffi>=1.0.0"],
)
复制代码

ABI 与 API

在二进制级别 (“ABI”) 访问C库充满了问题,尤其是在非 windows 平台上。

ABI 级别最直接的缺点是,调用函数需要经过非常通用的 libffi 库,它很慢(而且总是不能在非标准平台上完美通过测试)。API模式编译一个直接调用目标函数的 CPython C 包装器。相对而言,它的速度要快得多(而且运行得比 libffi 更好)。

选择API模式的更基本原因是,C库通常与C编译器一起使用。你不应该做诸如猜测结构中字段的位置之类的事情。上面的“真实示例”展示了CFFI如何在底层使用C编译器:示例使用 set_source(..., "C source...") 而不是 dlopen()。当使用这种方法时,我们有一个优势,我们可以在 cdef() 的不同地方使用字面上的 “……” ,缺失的信息将在C编译器的帮助下完成。CFFI 将把它转换为单个C源文件,其中包含未修改的 “C source” 部分,后面跟随着一些特殊C代码和 cdef() 派生的声明。当编译这个C文件时,生成的C扩展模块将包含我们需要的所有信息。就像往常一样,如果我们错误地声明了某个函数的签名,C编译器将给出警告或错误。

注意,set_source()中的 “C source” 部分可以包含任意的C代码。您可以使用它来声明一些用c语言编写的辅助函数。(你可以在“C source”部分,使用static C关键字)

例如,这可以用来将宏包装成更标准的C函数。额外的C层在其他方面也很有用,比如调用函数,这些函数需要一些复杂的参数结构,您更喜欢在C中构建而不是在Python中。(另一方面,如果您需要调用 “function-like” 宏,那么您可以直接在cdef()中声明它们,就好像它们是函数一样。)

转载于:https://juejin.im/post/5b8fd8d86fb9a05d232837b6

CFFI - ABI模式与API模式相关推荐

  1. Xbee Pro 900HP模块的API模式组网配置

    为使无人机编队个体间实现组网通信,需要选用合适的通信模块构建通信网络.前期曾试用ZIGBEE模块(DRF1605),但其传输速度不能令人满意--每秒最快只能接收10个数据包,320个字节,将近2.4K ...

  2. QIIME 2教程. 24Python命令行模式Artifact API(2021.2)

    Python命令行模式 Artifact API https://docs.qiime2.org/2021.2/interfaces/artifact-api/ 注:本指南假定您已执行"4人 ...

  3. QIIME 2教程. 24Python命令行模式Artifact API(2020.11)

    文章目录 Python命令行模式 译者简介 Reference 猜你喜欢 写在后面 Python命令行模式 Artifact API https://docs.qiime2.org/2020.11/i ...

  4. 第六节:框架搭建之EF的Fluent Api模式的使用流程

    一. 前言 沉寂了约一个月的时间,今天用一篇简单的文章重新回归博客,主要来探讨一下Fluent Api模式在实际项目中的使用流程. 1. Fluent API属于EF CodeFirst模式的一种,E ...

  5. .net ef 字段不区分大小写_第六节:框架搭建之EF的Fluent Api模式的使用流程

    一. 前言 沉寂了约一个月的时间,今天用一篇简单的文章重新回归博客,主要来探讨一下Fluent Api模式在实际项目中的使用流程. 1. Fluent API属于EF CodeFirst模式的一种,E ...

  6. XBee zigbee 使用指南---XBee API模式示例

    (http://www.bitconn.com/form_1/ 登记后,购买XBee模块,送USB评估底板及相关中文资料,或者免费申请借用评估套件) 目录 示例:配置本地XBee模块 步骤1:配置XB ...

  7. 【虚幻引擎UE】UE5 三种模式调用API详解(案例基于免费Varest插件)

    [虚幻引擎UE]UE5 三种模式调用API详解(案例基于免费Varest插件) 想通过UE5 调用API实现GET和POST, 可以通过自己编写C++方法, 或基于相关HTTP请求插件, 如Vares ...

  8. XBee zigbee 使用指南---XBee API模式介绍

    (http://www.bitconn.com/form_1/ 登记后,购买XBee模块,送USB评估底板及相关中文资料,或者免费申请借用评估套件) 目录 API模式的优点 API框架结构 起始符 长 ...

  9. Sentinel动态规则API模式命令【实战笔记】

    目录 一.动态规则API命令1.支持的API命令2.基本信息命令3.获取规则命令4.获取资源clusterNodeVO5.集群资源监控信息6.簇点链路7.获取origin clusterNode8.获 ...

最新文章

  1. mac攻略(1) -- 简单配置php开发环境
  2. Spring注解使用方法
  3. 第十三章 时间序列分析和预测
  4. ruby elixir_如何使用Elixir和Phoenix快速入门构建CRUD REST API
  5. GIS实战应用案例100篇(八)-桩号相同,坐标不同,RTK怎么输入曲线要素?
  6. REVERSE-PRACTICE-BUUCTF-14
  7. Opencv--IplImage访问图像像素的值
  8. liunx搭建sftp文件服务器,Centos7搭建sftp服务器
  9. Android 解决ViewPager双层嵌套的滑动问题
  10. 最速下降法/steepest descent,牛顿法/newton,共轭方向法/conjugate direction,共轭梯度法/conjugate gradient 及其他
  11. linux sata 3驱动下载,linux – SSD SATA3驱动器可能存在的问题
  12. linux下用c语言写吃金豆,吃金豆pacmanTC版
  13. 语音芯片IC几种输出方式
  14. Java+AutoCAD-坐标系转换
  15. eclipse Android添加权限
  16. SAP BOM物料清单详解
  17. JNI语法 JNI参考 JNI函数大全
  18. 只会Python可造不出iPhone
  19. win10更改固定IP出现意外无法更改
  20. 计算机网络实验教程钱德沛_计算机网络实验教程

热门文章

  1. python k线顶分型_顶分型底分型代码
  2. ft2232驱动安装方法_win7系统无法安装打印机驱动程序的解决方法
  3. 所有关于php上传,关于php文件上传
  4. 最新酷睿计算机配置,三款intel九代酷睿全系列组装电脑配置推荐 每一款CPU都支持超频...
  5. linux服务器运维操作命令,Linux服务器运维常用命令列表
  6. **Java有哪些悲观锁的实现_Redis 分布式锁的正确实现方式(Java 版)
  7. mysql -e -f_twitter-不正确的字符串值:'\ xF0 \ x9F \ x8E \ xB6 \ xF0 \ x9F ...'MySQL
  8. python自动化办公设置_python自动化办公之 python操作Excel
  9. delphi formshow 刷新_OPPO K7x部分配置和外观公布90Hz刷新率11·4发布
  10. BP神经网络和支持向量机在R语言中的实现