比特派官方网站首页|ffi

作者: 比特派官方网站首页
2024-03-07 18:41:31

FFI是什么 - 知乎

FFI是什么 - 知乎切换模式写文章登录/注册FFI是什么上古​阿里巴巴 从业人员FFI(Foreign Function Interface)是一种编程框架,它允许在一个编程语言中调用另一个编程语言编写的函数。换句话说,FFI 是一种机制,允许程序在不同语言之间交互,特别是在原生代码(如 C 或 C++ 编写的代码)与高级语言(如 Python、Ruby、Java 或 JavaScript)之间。FFI 的主要用途包括:性能优化:如果高级语言在某些计算密集型任务上性能不足,可以使用 C/C++ 等低级语言编写这些部分,并通过 FFI 调用以提高性能。代码重用:通过 FFI 可以在新项目中复用现有的低级语言库,而无需完全用高级语言重写。平台或硬件特定的功能:某些操作系统或硬件的特定功能可能没有高级语言的直接支持,但可以通过低级语言调用。FFI 允许开发者通过这些低级语言编写的函数来访问这些功能。遗留系统集成:FFI 可以用来与遗留系统进行交互,特别是当这些系统是用不同的编程语言编写且不易更改时。在使用 FFI 时,通常需要以下步骤:定义外部函数的签名,使得高级语言知道如何映射和调用低级语言的函数。确保外部函数是“可见的”,这通常意味着需要将其编译为共享库(如动态链接库 DLL 或共享对象 SO)。在高级语言中加载和调用这些外部函数。FFI 通常需要处理数据类型的转换,因为不同的语言可能会以不同的方式表示和存储数据。此外,FFI 也可能带来安全风险,因为直接调用低级语言函数可能会绕过高级语言的内存安全保证,因此使用 FFI 时需要小心谨慎。不同的编程语言和平台提供了不同的 FFI 工具和库。例如,在 Python 中,有 ctypes 和 cffi 库可以用来实现 FFI;在 Rust 中,有 bindgen 和 cc 等工具帮助与 C 语言进行交互;在 Node.js 中,有 node-ffi 库来调用 C 语言库的函数。注意:函数重载对FFI不友好,原因如下:因为许多用于 FFI 的语言和环境(尤其是 C 语言)不支持函数重载,而函数重载是一种允许在同一作用域内多次声明同一函数名但参数类型或数量不同的语言特性。在 C++ 中支持函数重载,这意味着可以有多个具有相同名称但参数列表不同的函数。例如:void foo(int a);

void foo(double a);

void foo(int a, double b);上面的每个函数都有不同的参数类型和数量,但都叫做 foo。然而,C 语言不支持函数重载,每个函数都必须有一个独一无二的名称。当使用 FFI,特别是与 C 语言接口进行交互时,所有的函数调用都必须准确地指定要调用的函数。在 C 语言中,每个函数签名都必须映射到一个唯一的符号名称。因此,C++ 的函数重载必须通过某种方式解决这个问题,使得每个重载函数都有一个独特的符号。在实际中,这通常意味着以下几点:名称修饰(Name Mangling):C++ 编译器通过一个叫做名称修饰(或名称变形)的过程,将每个重载函数的名称转换为包含函数参数类型信息的唯一名称。这使得每个函数在链接时都有一个不同的符号,但这些修饰后的名称对于 FFI 来说通常是不可见或者难以使用的。extern "C":在 C++ 中,你可以使用 extern "C" 告诉编译器按照 C 语言的规则对函数进行编译(即不进行名称修饰),从而使得这些函数可以通过 FFI 被其他语言调用。但是,由于 C 语言不支持重载,你不能在 extern "C" 块中重载函数。extern "C" {

void foo(int a);

// void foo(double a); // 这会导致编译错误,因为 C 语言不支持重载

}手动重命名:如果你需要使用函数重载,并且想要通过 FFI 导出这些函数,你必须手动给每个重载版本一个独一无二的名字:extern "C" {

void foo_int(int a);

void foo_double(double a);

}总结来说,由于函数重载在 FFI 交互中的限制(特别是与 C 语言的互操作性),通常需要额外的步骤来解决名称冲突问题,确保每个函数都有一个唯一的符号,这样它们才能在不支持重载的环境中使用。PS:文中如有问题,欢迎指正发布于 2024-01-23 08:03・IP 属地浙江ffi​赞同​​添加评论​分享​喜欢​收藏​申请

Rust FFI 编程 - FFI 概述 - Rust语言中文社区

Rust FFI 编程 - FFI 概述 - Rust语言中文社区

Rust语言中文社区

Search  

RSS  

帐户

< 返回版块

Rust FFI 编程 - FFI 概述

Mike Tang

发表于 2020-04-09 21:03

Tags:rust,ffi

FFI(Foreign Function Interface)是这样一种机制:用一种编程语言写的程序能调用另一种编程语言写的函数(routines)。

FFI 有两种内涵。一种是是在当前正在使用的语言(host)中,调用由其它语言(guest)提供的库。第二种内涵与第一种方向相反,即,使用当前语言(host)写库,供其它语言(guest)调用。不过,后者不是任何语言都能做到的,有些语言即使能做,也会非常吃力。

FFI 的历史和现状

FFI 这个术语最早来自 Common Lisp 的规范。目前几乎所有严肃编程的语言都有提供 FFI 的支持,但大多数是单向功能。

不同语言称呼这种语言间调用的功能名字可能不同。Common Lisp、Haskell、Python、Rust 这些叫 FFI,Java 叫 JNI 或 JNA,还有一些其它语言叫 “绑定”。严格来说,FFI 与 绑定,意义并不相同,绑定可以理解为 FFI 中的一种实现。

不同语言实现 FFI 的方式不尽相同。有的语言,比如,要调用 C 库,必须用 C 语言,按那种语言的绑定规范,实现一个 C 项目,用 C 编译器编译并链接,生成库文件,再由这种语言调用(这种语言本身已经实现了加载其定义的规范 C 库的能力)。

有的语言,比如,Rust,要调用 C 库,不再需要使用 C 语言写绑定工程,而是直接使用 Rust 语言写。这样,就有个好处是,你不再需要掌握 C 语言的那么多的繁文缛节和工具链(但是还是必须懂 C 语言)。

FFI 调用原理

为什么不同的语言之间能互相调用呢?

我们知道,计算机的运算,最底层的数据/代码都是以二进制的形式存在。所有的语言在编译后,都会以二进制的形式去执行(即使编译后的代码为字节码,虚拟机在运行的时候,也会继续翻译成 CPU 认识的二进制指令)。这就为不同语言间的调用提供了可能性。

但是,可能归可能。二进制毕竟太底层了。没有大家一致认可的调用约定,那也是不可能互通的。于是,ABI(应用程序二进制接口) 就出现了。调用约定,类型表示和名称修饰这三者的统称,即是众所周知的应用二进制接口(ABI)。

试想,如果所有的语言在调用时都能认识同样一套 ABI 规范,那么就能完全畅通的调用了。可惜,世界不会像我们人为想象的那样干净。

在计算机技术发展的过程中,出现了各种 ABI 规范,它们有的看起来相似,但在具体编译器的实现上,又有细微不同。所以,这是一件很麻烦的事情。大体来说,有如下规范:

cdecl

syscall

optlink

pascal

register

stdcall

fastcall

thiscall

winapi

Intel ABI

System V

等。详情可参考:X86调用约定。

而 Rust 目前支持如下 ABI 约定:

stdcall

aapcs

cdecl

fastcall

vectorcall

Rust

rust-intrinsic

system

C

win64

sysv64

不过,值得庆幸的是,目前我们 IT 工业的基石,绝大部分是由 C 语言写成。于是自然而然,绝大多数库都遵循 cdecl(或 C)规范。所以我们可以专注于 C 规范来讨论问题。

FFI 的困难之处

FFI 实现起来,比想像的要复杂许多,困难体现在:

如果 host 语言(调用主动方)带 GC(垃圾收集器),而 guest 语言(调用被动方)不带,那么可能会在资源管理(创建,释放)上面造成一些问题,需要特别细致地处理;

复杂对象或类型,在映射到两边的时候,可能会有一些不协调甚至失真的现象;

两边要同时引用一个可变对象的时候,可能会遇到问题;

如果两边的语言都是运行在 VM 之上的语言,那么这两个语言之间的直接 FFI 非常困难甚至不可能;

类型系统/对象组合模型/继承机制等其它细节,可能在跨语言的时候,成为障碍;

其它。

所以,虽然都能做 FFI,但是不同语言实现 FFI 的困难程度是不同的。

哪些语言可以方便地对外提供 FFI 库支持

可惜,大部分语言只能单向地“索取”。目前所知,能(较方便地)对其它语言提供 FFI 库支持的语言有:

C

C++(通过定义 C 接口)

Rust(通过使用 C 约定)

Ada

Fortran

小编能力所限,如有未列举完整之处,欢迎补充。

偷懒的程序员

在开发的过程中,要一个一个对大量的 C/C++ 库写绑定来进行 FFI,毕竟是一项费时费力的活儿。聪明的程序员们就开始构想一些“通用”的方案,实现批量快速绑定。

SWIG

以下定义来自 https://zh.wikipedia.org/wiki/SWIG:

简单包装界面产生器(SWIG)是一个开源软件工具,用来将C语言或C++写的计算机程序或函式库,连接脚本语言,例如Lua, Perl, PHP, Python, R, Ruby, Tcl, 和其它语言,例如C#, Java, JavaScript, Go, D, OCaml, Octave, Scilab以及Scheme. 也可以输出成XML格式。

也就是说,使用了 SWIG 这套工具和规范,就可以直接在上层语言(动态语言居多)中调用 C/C++ 库了,省却大量烦恼。但在实际使用中,还会有一些细节问题,往往需要人工调整。所以也不是那么完美。

SWIG 官网:http://swig.org/ 。

Gnome 社区关于构建通用 GI 规范的理想和实践

Gnome/Gtk 那一帮理想主义青年,发明了 GI(GObject Introspection)。用于对基于 glib/gobject 生态的众多软件(C 代码库)自动生成完整的接口描述文件(及 typelib),然后其它语言只要实现了对 Gir 这一个标准的支持,那么就可以无缝调用所有经过 Gir 化处理的 C 库。而不再需要单独为每一个 C 库做绑定了。这样就大大简化了 FFI 接口项目的编写工作。

目前这一杰出创意的重量级工作成果有 cairo, pango, gtk 等库。

更多信息请参考:https://gi.readthedocs.io/en/latest/。

另一种思路——基于字节码的平台级路线

语言间的相互调用,历史的发展提供了另一条路线:建立一个共同的字节码平台,这个平台之上的所有语言,皆可便捷地相互调用。

JVM 平台语言之间的 FFI

Java 发展到现在,已经形成了一个强大的 JVM 生态。JVM 平台上有大量的新语言产生,比如 Scala, Clojure, JRuby, Jython 等。这些语言前端不同,但是共享同一套 JVM 字节码和调用规范。因此,这些语言和 Java 之间,以及这些衍生语言之间,能比较容易地实现相互调用。

JVM 平台的缺点在于,其生态中的成果,被局限在了 JVM 平台内,无法(或很难)被其它语言平台所享用。

WASM 平台的 FFI

Web Assembly(WASM)是一个新的字节码平台,其势头发展很猛。其有着比 JVM 平台更大的野心和联盟。因为是新设计的字节码,故其在设计的时候,就对 JVM 平台的一些问题做了规避(这方面可 Google 查阅相关资料)。

目前几乎所有主流语言都已实现将 WASM 作为编译目标,并且有相当一部分语言能够加载 WASM 库文件,调用其中的函数。不同的语言编译出的 WASM 效能和体积大小也是不同的。目前来看,C、C++、Rust 这些非 GC 语言能够编译出最精简,执行效率最高的 WASM 字节码。

WASM 的规范还在快速完善中。

结语

本篇描述了 FFI (外部程序接口)的概念和基本原理,并对其历史、内在的困难,以及程序员在 FFI 发展上的各种尝试,都做了简单介绍。

本篇大量内容参考 wikipedia 的 Foreign function interface 页面。

恕小编能力所限,如有描述不当或不完整之处,欢迎同行指正或补充,感谢!

评论区

写评论

AndyJado

2022-07-30 19:21

谢麦老师, 我竟觉得看得懂.

bell152

2022-07-29 15:46

在Mac下一直从java一直调不通rust....

phper-chen

2020-04-16 14:15

666 不过都用上rust了,就不再想跟c++打交道了,哎

心情好又暖

2020-04-10 17:33

麦克老师要是把整个系列都更完,那这就成为了Rust中文界的元老级教程哇 期待后续更新,跟进学习

chenwei767

2020-04-10 11:31

学习了!

Nalleyer

2020-04-09 21:47

好文,醍醐灌顶

1

共 6 条评论, 1 页

友情链接:

泰晓科技

| Ruby China

| 电鸭远程社区

| IPFS中文社区

鸣谢:

迅达云

赛贝

LongHash

©2016~2020 Rust.cc 版权所有   

Powered by

Forustm &

Rusoda &

Sapper

蜀ICP备20010673号-1

W-8BEN-E如何填?先确定你的实体类别 - 知乎

W-8BEN-E如何填?先确定你的实体类别 - 知乎首发于离岸气象切换模式写文章登录/注册W-8BEN-E如何填?先确定你的实体类别叶莹 FATCA的主要目的,无非是希望透过要求美国以外的金融机构揭露其下美国纳税义务人的资产状态,达到促使他们诚实申报以及追讨欠税的双重目的。FATCA实施以来,一直有客户收到银行或金融机构的通知,要求其证明是否为FATCA的应申报实体,会进一步索取FATCA身份自证文件,即要求客户提供填妥的W-8BEN-E表格。本文主要介绍FATCA的各种实体分类,以便客户清楚了解各种实体面临的申报问题,帮助客户更好地理解表格上的各项内容以免错报。 FATCA把美国以外的所有实体分为FFI和NFFE两大门类。NFFE是以FFI的反面来自我定义的,换言之,一个美国以外的实体如果不是FFI,那就是NFFE。一、

FFI——符合以下任何一个标准的非美国实体均属FFI:1. 储蓄机构(即一个银行或类似实体在正常运营中会接受储蓄存款);2. 托管机构(即一个实体业务中重要组成部分包括为第三方代为持有金融资产);在进一步定义“重要组成部分”方面,要求该实体最少20%的总收入来自于或可追溯到持有金融资产及相关金融服务。譬如说,最少20%的总收入来自于托管费、账户管理费、转款费,以及由执行证券交易产生的佣金和费用等。3. 投资实体;投资实体又进一步定义为符合以下任何一个标准的非美国实体:a)

一类投资实体—总收入中最少50%来自于为客户或代表客户从事以下任何一类活动:金融工具的交易;专项或集合投资组合的管理;或代表他人处理或管理基金、资金或金融资产或者b)

二类投资实体——超过50%的总收入来自于金融资产,且该实体由另一个储蓄机构、托管机构或一类投资实体所管理;或者该实体对外宣称是一个集合投资工具、共同基金、交易所上市基金、私募基金、对冲基金、风险投资基金、杠杆收购基金,或以对金融资产进行投资、再投资或交易为投资战略的其他类似投资工具。特定保险公司:一般而言,指有义务就现金价值保险或年金合约进行支付的保险公司或保险集团。4. 某些控股公司及财资中心:对于投资基金本身而言,除了主权投资基金和其他由政府控制或持有的基金以外(此类基金另属豁免例外),基本上都属于以上三类投资实体的其中一类,所以基本上都是FFI。 值得注意的是,一个投资基金往往从属于一个复杂的投资结构,譬如说基金本身可能以有限合伙制企业的形式建立,则基金的有限合伙人和普通合伙人则需另做分析,判断它到底是FFI还是NFFE。 FFI必须遵守FATCA,配合提供其客户的账户资料给美国税务局;如不配合,则此FFI账上的美国来源所得将被课以30%惩罚性扣缴税。二、

一个实体如果不是FFI(不管是否出于自我选择),则是NFFE。 NFFE,非金融外国实体,是指不属于FFI的外国实体。NFFE必须归类为例外NFFE、主动NFFE和被动NFFE。被动NFFE需向其所得税代扣义务人提供有关其全部实质性美国所有人(直接或间接持有超过10%权益)的特定信息(名称、地址和税务编号),如果没有实质性美国所有人,则须提供相应证明。而按照IGA,此10%权益实证标准通常需要替换为实际持有25%或更多权益的“控制人”。1. 符合例外条件的NFFE•

包括直接或间接由善意取得美国居民身份的居民完全持有、在NFFE的组织国家存续的上市公司和关联公司、区域性NFFE或主动NFFE。•

这些类型的实体由于其自身活动特点而通常不可能被美国自然人/法人用于隐瞒资产。2. 主动NFFE•

实际进行业务活动而不是持有能产生投资收益(例如利息、分红、租金等)的资产的实体。•

满足下列条件的实体可归类为主动NFFE:•

上一财政年的总收入中被动收入低于50%;且•

产生被动收入的资产或为了产生被动收入而持有的资产在持有资产(按季度测试)中的加权平均百分比低于50%。 简单来说,如果一个NFFE少于50%的收入来自于持有产生被动收入(如红利和利息)的资产,则该NFFE属于主动NFFE(Active NFFE),譬如说,一个超过50%的收入来自于制鞋生产活动的鞋厂,属于主动NFFE。3. 被动NFFE•

任何不符合例外条件或不属于主动NFFE的NFFE,即如果一个NFFE超过50%的收入来自于持有产生被动收入的资产,则该NFFE属于被动NEFE。 被动NFFE需向其所得税代扣义务人提供有关其实质性美国所有人(如有)的证明;如果没有实质性美国所有人,也需提供相应证明。 对于普通客户而言,如果拥有非美国本地的公司或实体,并在金融机构开有账户,因FATCA被金融机构要求提供W8-BEN-E表时,应该结合自身实际经营状况申报。如果申报为主动NFFE,则可免于继续申报,或可避免因为被认定为被动NFFE申报不及时或文件不合规造成账户被冻结或关闭的风险。编辑于 2016-10-08 13:15税务法律美国银行​赞同 78​​21 条评论​分享​喜欢​收藏​申请转载​文章被以下专栏收录离岸气象离岸政策动向。注定了这里都是悲伤的

ffi - 知乎

ffi - 知乎首页知乎知学堂发现等你来答​切换模式登录/注册ffi暂无话题描述关注话题​管理​分享​讨论精华视频等待回答外部函数接口 FFI —— 虚拟机中重要但不起眼的组件Chill Magic梦想创造一门新的语言——或许你没听说过它,但是,它为虚拟机的世界与二进制的世界搭起了一道桥梁。它就是FFI。 一个看似简单的问题用过 Scheme, Python 等各种动态语言的人,可能会对里面的 apply 情有独钟。 Scheme 中,你可以这样写: (define (add-int a b) (+ a b)) (add-int 5 6) (apply add-int '(5 6))Python2 中,你可以这样写: def add_int(a, b): return a + b add_int(5, 6) apply(add_int, (5, 6))当然这在 Python3 中更改了,要写成…阅读全文​​赞同 145​​32 条评论​分享​收藏从 Rust 库中公开 FFISINPLEWikipedia 将 FFI 定义为一种机制,通过这种机制,用一种编程语言编写的程序可以调用或使用用另一种编程语言编写的服务。FFI 可用于加快程序执行(这在 Python 或 Ruby 这类动态语言中很常见),或者只是因为你想使用一些其他语言编写的库(例如 TensorFlow 的核心库是用 C++ 写的,并暴露了 C API,允许其他语言使用)。为 Rust 库编写 FFI 并不难,但是却有一些挑战和可怕的部分,主要是你要使用指针和 unsafe 块1。这可能会脱…阅读全文​​赞同 49​​6 条评论​分享​收藏最强nodejs下C++绑定方案介绍车雄生xLua,InjectFix,puerts的作者最近基于 puerts 做了个nodejs addon,能让nodejs方便的调用c++的库。拿一个比较知名的同类方案v8pp做对比:相同点 都是基于C++模板技术提供了声明式绑定API。都能支持nodejs和其它v8环境先列几个不同点 v8pp提供了包括v8的初始化,设置,c++/js交互等封装,而puerts仅仅专注于c++/js交互一项。声明要绑定c++ api后,puerts能生成这些c++ api的TypeScript声明(.d.ts文件),这似乎是首创puerts对c++特性支持丰富些,比如支持函数…阅读全文​​赞同 44​​26 条评论​分享​收藏什么? C 语言动态库免费大放送了?Karminski-牙医码农 | homelab级垃圾佬 | 前掘金技术总监(如果你自己懒得写代码, 可以用我的demo: https://github.com/karminski/Package-C-Library-for-Luajit-the-FFI-Method ) 不要慌, 懒是一种美德. 看到有同学说 Lua 库少, 需要自己造轮子. 其实不是这样的, 今天给大家看一个魔法, 这个魔法可以让你非常方便的在 luajit 里面使用高性能的 C/CPP 库, 从而避免自己造轮子的痛苦. 这个魔法是 FFI ( Foreign function interface ), 我并不打算仔细讲 FFI 原理, 所以简单来说, FFI 实现了跨语言的二进制接口. 它的优点是高效方便. 直接调用 ABI…阅读全文​​赞同 35​​1 条评论​分享​收藏在 NodeJS 中与 C++ 代码通信政子SDN & Web & OS最近在开发中遇到需要在 NodeJS 中调用 C++代码的问题,在此略作总结。 主要方案在 NodeJS 中,和其他语言编写的代码通信主要有两种方案: 使用 AddOn 技术,使用 C++为 NodeJS 编写一个拓展,然后在代码中调用其他语言所编写的源码 or 动态库使用 FFI(Foreign Function Interface)技术,直接在 Node 中引入其他语言所编写的动态链接库在对这两种方式进行比较后,发现这两种方式各有优劣。 首先,AddOn 技术比较通用,它可以使…阅读全文​​赞同 35​​14 条评论​分享​收藏我用Rust给Node.js增加了ffi能力张宇昂http://doc.ssr-fc.com/ffi-rsA module written in Rust and N-APi provides interface (FFI) features for Node.js 简介 ffi-rs 是一个使用 Rust 编写用于在 Node.js 中使用 ffi 来调用 C++/C/Rust 等语言的能力。开发者无需编写 C++ 代码便可以直接在 js 中调用其他语言的能力。此模块在功能上尽量对标node-ffi 模块,但底层代码已彻底重写。因 node-ffi 模块已经多年无人维护处于一个不可用的状态因此开发了ffi-rs模块。安装$ npm i ffi-rs目前支持的…阅读全文​​赞同 25​​6 条评论​分享​收藏Rust FFI (C vs Rust)学习杂记灵山行者一入江湖无踪影,归来依旧少年郎!pdf版本: https://github.com/yujinliang/my_writing 前言"FFI"是" Foreign Function Interface "的缩写,大意为不同编程语言所写程序间的相互调用。鉴于C语言事实上是编程语言界的万国通,世界通用语,所以本文主要围绕着C和Rust之间的互通来学习。单刀直入,话不啰嗦,好比学外语, 先要从认字开始, 对于编程语言来说就是各种“基础类型”, 因为类型代表了:可操作集和布局, 有人会疑问“类型布局”是个什么东西?! 好吧, 换个词“房屋布局”,…阅读全文​​赞同 22​​2 条评论​分享​收藏JitFFI —— 对于外部函数接口FFI的Jit编译器Chill Magic梦想创造一门新的语言什么是 JitFFI ?JitFFI 是一个函数库,它内含一个 Jit 编译器,能够通过 Jit 的方式编译 外部函数接口(FFI, Foreign Function Interface)。 或许你并不了解 FFI,但是如果你是解释型语言的使用者,你一定接触过它。 首先我们将目光放在 C/C++ 语言中。 假设我们有一个 动态链接库 libL.so,其中有一个 add_int 函数: int add_int(int a, int b) { return a + b; }想要在 C/C++ 程序中调用这个函数,只需要知道加载链接库后函…阅读全文​​赞同 23​​4 条评论​分享​收藏用cocos2dx并选择lua开发游戏的时候,为什么要使用tolua绑定c/c++代码,而不直接用luajit的ffi?我是妖怪游戏开发FFI 只能绑定 C 的结构和函数,所以要用 FFI 来绑定 C++ 接口,那么需要编写一个 C++ -> C 的封装层。 具体参考: LuaJIT FFI/C++ binding, best approach? 另外 FFI 需要 JIT 才能获得良好的性能,而在 iOS 上由于苹果的限制是无法启用 JIT 的,此时 FFI 性能就不够理想了。 ------ 华丽的分割线 ------ 不过这些都不是根本原因。其实也就是懒,tolua++ 多省事儿啊,所以一直用到现在。 今年计划用一个新 luabinding 来替代 tol…阅读全文​​赞同 22​​5 条评论​分享​收藏​喜欢project Panama中的ffi是怎么实现的?dreamlike-ocean笨蛋 问题的关键是状态机Panama实现原理浅析本文基于JDk21的api,参考JEP为 https://openjdk.org/jeps/454 由于FFI与架构息息相关且本人水平一般,所以这里只考虑Linux x86_64架构的FFI,即参考System V ABI AMD64 JDK源码参考 https://github.com/openjdk/jdk21 笔者OS Linux dreamlike-MS-7E01 6.2.0-33-generic #33-Ubuntu SMP PREEMPT_DYNAMIC Tue Sep 5 14:49:19 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux System V ABI手册参考 https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf 参数分配对于Linux o…阅读全文​​赞同 18​​添加评论​分享​收藏​喜欢《百年孤独》中丽贝卡给马孔多居民带来的失眠症是现实中我们常说的FFI吗?花脸钟离​广播行业 制作人[图片] 《百年孤独》中的失眠症并不是现实中的FFI(Fatal Familial Insomnia,致死性家族失眠症)。 现实中的FFI是一种罕见的遗传性疾病,其症状包括逐渐恶化的失眠、自主神经功能障碍、运动障碍等。虽然FFI也会导致失眠,但其病理机制与《百年孤独》中的失眠症并不相同。 在小说中,失眠症被描述为一种神秘的病症,其症状包括逐渐失去记忆、遗忘过去,甚至最终忘记自己的身份。 这种病症在小说中是一种隐喻,是一个象征,代表着马孔多…阅读全文​​赞同 20​​添加评论​分享​收藏​喜欢关于Rust封装C++接口需要注意的地方知乎用户HjOyI4这篇文章是用来讲述Rust封装C++的动态依赖库的一些坑吧 当然可能没啥技术含量~~~ 我是C++小白 哈哈哈哈 要求:因为要封装 C++的dll, 要Rust能够调用到C++的动态依赖库这个好像在国内我没搜到啥相关的有效的资料吧 QAQ, 挺难受的, 简单的 Rust的基础就不讲了, 我把几个坑点说下,下面的内容是建立在你有一些c++的基础上哈 关于封装接口的库的选择 这里我推荐[rust_bindgen]( rust-lang/rust-bindgen ), 这个项目能够为你的头…阅读全文​​赞同 14​​4 条评论​分享​收藏Aurix TC27X 结合ETAS RTA-OS实现不同ASIL软件的FFISgnesTalk is cheap,show me the data简介本文以 Infineon Aurix TC27xT MCU 结合ETAS RTA-OS 实现的ISO 26262 要求的freedom from interference功能做一个简单的介绍。 Freedom from interference Definition in ISO 26262 part 1: [图片] FFI的目的:低ASIL等级的模块出错不会导致高ASIL等级的模块的错误,为了能够达到FFI的目的,需要对不同ASIL等级的软件做software partition,然后结合硬件的memory protection机制来实现FFI。 模块之间有三种数据交互方式:read/writ…阅读全文​​赞同 10​​12 条评论​分享​收藏PHP 中文工具包 ChineseUtil v2.0 发布,引入 FFI 提升性能节省内存宇润​软件开发行业 从业人员ChineseUtil 是 PHP 中文工具包,支持汉字转拼音、拼音分词、简繁互转、数字转换、金额数字转换。 由于中文的博大精深,字有多音字,简体字和繁体字也有多种对应。并且本类库返回的所有结果,均为包含所有组合的数组。 本类库字典数据总共收录 73925 个汉字,包括:3955 个简体字,1761 个繁体字,68209 个其它汉字。 Github: Yurunsoft/ChineseUtil 更新日志v2.0.0 (2020-08-17) 支持 FFI、Swoole FFI 重构拼音分词算法 拼音…阅读全文​​赞同 9​​1 条评论​分享​收藏手把手教你写 Dart ffi阿里云开发者​已认证账号本文以step by step的方式说明了Dart ffi的使用,适合新手学习。作者 | 安秋亮(汘浪) 来源 | 阿里开发者 什么是ffiffi是 foreign function interface[1]的缩写,是一种机制。通过这种机制,用一种编程语言编写的程序可以调用另一种编程语言编写的程序或服务。像我们熟悉的Java JNI便是ffi机制。 创建sample工程本文示例是在macos创建并运行的,最终的产物是mac可执行程序。 flutter --version Flutter 3.3.0 • channel stable …阅读全文​​赞同 8​​2 条评论​分享​收藏ffi 一定要有序列化开销吗?北南​编程语言话题下的优秀答主序列化是把内存对象变成字节流,主要是给需要网络传输或持久化用的。所以ffi可以通过序列化做,也可以不通过。这取决于不同语言之间如何通讯,如果能互相直接访问内存了,那可以避开序列化,但会带来还很多不稳定因素和安全隐患。 序列化有序列化的好处,可以更好的隔离和解耦。直接访问内存未必性能就一定好,rust和c的整合的确是非常好的,但并不具备普遍性。其他语言这么搞,要多加小心。阅读全文​​赞同 8​​1 条评论​分享​收藏​喜欢project Panama中的ffi是怎么实现的?Makai浅析一下Java FFM API(Project Panama) 这篇文章并不是讲如何使用Java FFM API,而是浅谈其背后的实现原理。前言前不久,OpenJDK宣布了 Java Foreign Function & Memory API将在JDK 22退出预览,这意味着在JDK 22后,FFM API不会有重大改动。借此机会,我想可以好好聊聊FFM API是怎么实现的。FFM API介绍FFM API由两大部分组成,一个是 Foreign Function Interface,另一个是Memory API。前者是外部函数接口,简称FFI,用它来实…阅读全文​​赞同 6​​7 条评论​分享​收藏​喜欢Python 通过FFI调用C/C++代码深入编译原理微信公众号「sntflyv」动态链接库调用确定函数声明和ABI(二进制接口),就能够确定参数和返回值的传递方式。 FFI的传递方式是基于函数声明和ABI的固定不变的东西,再加上能够变化的 函数地址、数据,就可以调用这个函数。 [图片] 编写C语言接口// ffi_test.cpp extern "C" int add(int a, int b); int add(int a, int b) { return a + b; }编写Python程序调用动态链接库# test_ffi.py from cffi import FFI ffi = FFI() ffi.cdef(""" int add(int a, int b);…阅读全文​​赞同 6​​1 条评论​分享​收藏使用Dart FFI在Flutter桌面应用中集成C库xioxinFlutterվ'ᴗ' իAngular; 学习Rust中原文:《Integrating C library in a desktop Flutter app using Dart FFI》 原文发布时间:2021 年 12 月 15 日 封面图片来自 Unsplash 的 Maksym Zakharyak 这篇文章是我 上一篇文章 (中文 )的后续,我在这篇文章中讲解了如何在Android和iOS的Flutter应用程序中整合C++语言的OpenCV库。已经过去一年了,从那之后发生了很多变化。框架的第二个主要版本已经发布了。Flutter 2.0不仅引入了Dart FFI的稳定版本和期待已久的空安全(…阅读全文​​赞同 5​​2 条评论​分享​收藏Node调用DLLmuniz前端、node、安全1、DLL介绍DLL(Dynamic Link Library)文件为动态链接库文件,又称"应用程序拓展",是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件。 2、Node 怎么调用DLL使用 node-ffi 模块…阅读全文​​赞同 5​​添加评论​分享​收藏浏览量20.2 万讨论量280  帮助中心知乎隐私保护指引申请开通机构号联系我们 举报中心涉未成年举报网络谣言举报涉企虚假举报更多 关于知乎下载知乎知乎招聘知乎指南知乎协议更多京 ICP 证 110745 号 · 京 ICP 备 13052560 号 - 1 · 京公网安备 11010802020088 号 · 京网文[2022]2674-081 号 · 药品医疗器械网络信息服务备案(京)网药械信息备字(2022)第00334号 · 广播电视节目制作经营许可证:(京)字第06591号 · 服务热线:400-919-0001 · Investor Relations · © 2024 知乎 北京智者天下科技有限公司版权所有 · 违法和不良信息举报:010-82716601 · 举报邮箱:jubao@zhihu.

一文读懂:致死性家族性失眠症的最新诊断标准 - 丁香园

一文读懂:致死性家族性失眠症的最新诊断标准 - 丁香园

丁香无线

丁香园论坛

丁香医生

新浪微博

丁香云管家

丁香智汇

丁香人才

RSS

登录

第三方登录

注册

论坛

神经

 

骨科

 

肿瘤

 

心血管

 更多

胸外

肾内

风湿免疫

感染

呼吸

消化

内分泌

论文基金

药品汇

健康互联

丁香六度

会议

医疗器械

检验

妇产

儿科

泌尿

麻醉

影像

普外

整形

眼科

神外

医院汇

精神

皮肤

口腔

重症

耳鼻喉

康复

丁香公开课

超声

血液

丁香园

神经

频道首页

最新资讯

指南共识

临床综述

经典病例

科室巡礼

神经病学时间

丁香公开课

RSS

一文读懂:致死性家族性失眠症的最新诊断标准

2018-07-31 09:30

来源:丁香园

作者:幸福的味道

字体大小

-

|

+

致死性家族性失眠症(FFI)是一种严重且罕见的朊蛋白病,由于临床症状存在异质性,因此诊断方面存在一定的困难。近期由我国宣武医院王玉平教授联合国内多位专家以及加拿大 Serge Gauthier 教授等发表了 FFI 临床诊断标准的专家共识,发表在最新的中华医学杂志英文版中。该诊断标准的要点编译如下:致死性家族性失眠症的核心临床特征FFI 患者的核心临床特征可以分为三组症状,各组症状及其临床出现的频率见下表:A 组症状(躯体性睡眠障碍):失眠、喉部喘鸣、睡眠相关的呼吸困难以及与睡眠相关的不自主运动;B 组症状(神经精神症状):快速进展性痴呆(RPD),伴或不伴有共济失调、锥体束征或锥体外系症状/体征以及精神症状;C 组症状(进展性交感神经性症状):高血压、出汗、心动过速、呼吸不规律以及构音障碍等。表 1. FFI 患者的临床特征        致死性家族性失眠症的临床诊断标准根据 FFI 患者的临床特征、家族史以及实验室检查结果,将临床诊断分为三种可能:可能的 FFI,很可能的 FFI 以及确诊的 FFI。三者的诊断标准如下:1. 可能的 FFI 诊断标准:躯体睡眠相关障碍(A 组症状)+1 或 2 项其他核心特征(B/C 组症状):A. 躯体相关睡眠障碍:失眠、深睡眠丧失、片段睡眠以及 REM 睡眠减少或丧失,喉部喘鸣、睡眠呼吸紊乱以及不自主运动;B. RPD:伴或不伴有共济失调,锥体束征或锥体外系症状/体征以及精神症状;C. 进展性交感神经性症状:高血压、出汗、心动过速、呼吸不规律。2. 很可能的 FFI 诊断标准:如果以下提示性特征中出现一项或多项,且出现以上两项或以上核心特征(A/B/C 组症状),则可诊断为很可能的 FFI。这些提示性特征包括:a. RPD 以及失眠的阳性家族史b. 躯体性失眠,睡眠相关呼吸困难,喉部喘鸣以及由多导睡眠图证实的不自主运动c. SPECT 或 PET 成像显示丘脑葡萄糖摄取减低3. 确诊的 FFI 诊断标准:如果朊蛋白基因(RPNP)检测结果为阳性,则可确诊 FFI。RPNP 基因检测结果显示:D178N 基因突变,且伴有 129 密码子蛋氨酸多态性。

查看信源地址

编辑:

陈珂楠

版权声明

本网站所有注明“来源:丁香园”的文字、图片和音视频资料,版权均属于丁香园所有,非经授权,任何媒体、网站或个人不得转载,授权转载时须注明“来源:丁香园”。本网所有转载文章系出于传递更多信息之目的,且明确注明来源和作者,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。同时转载内容不代表本站立场。

更多 >

高血压相关文章

os

精彩幻灯:高血压防治科普讲座

os

全国政协委员冯丹龙:呼吁加强职场人士高血压防治

os

大咖时间:蒋雄京教授专访之高血压指南应该用哪个

更多 >

失眠相关文章

os

口腔学霸的自我修养:早睡早起成绩好

os

采取错误的压力应对方式会增加失眠的风险

os

促食素受体拮抗剂可以用来治疗失眠

心动过速相关文章

os

心跳乱了节奏,该如何解救?

os

精彩幻灯:ECG 监护及常见异常心电图的识别

os

OCC 2018 | 施仲伟教授:重点解读 ACC 最新研究成果

近期热门文章

os

真心发问:如何洗干净一条黄黄的内裤?

os

鼻炎治不好,为什么还要看医生

os

为什么现在患腺样体肥大的孩子越来越多?

os

os

os

os

os

关注频道微信

纵览临床新进展

丁香园旗下资讯平台,注重临床思维养成。神经时间,注重神经科医生的需求。

关注频道微博

快速获悉最新信息

App下载

下载医学时间

每天10分钟成学霸

X

关注我们

手机扫一扫

关注丁香园微信号

丁香园旗下网站

丁香园

用药助手

丁香医生

丁香通

文献求助

Insight数据库

丁香人才

丁香导航

合作案例

丁香会议

丁香无线

丁当商城

调查派

丁香搜索

丁香云管家

丁香播咖

智能皮肤

医院汇

关于丁香园

关于我们

友情链接

联系我们

加入丁香园

网站声明

资料下载

资格证书

官方链接

丁香医生

丁香园新浪微博

FFI(语言交互接口(Foreign Function Interface))_百度百科

语言交互接口(Foreign Function Interface))_百度百科 网页新闻贴吧知道网盘图片视频地图文库资讯采购百科百度首页登录注册进入词条全站搜索帮助首页秒懂百科特色百科知识专题加入百科百科团队权威合作下载百科APP个人中心FFI是一个多义词,请在下列义项上选择浏览(共3个义项)添加义项收藏查看我的收藏0有用+10FFI播报讨论上传视频语言交互接口(Foreign Function Interface)本词条缺少概述图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧!FFI,中文名为语言交互接口(Foreign Function Interface),一个可以在某种计算机语言中调用其它语言的接口。中文名语言交互接口外文名FFIFFI(Foreign Function Interface)是用来与其它语言交互的接口,在有些语言里面称为语言绑定(language bindings),Java 里面一般称为 JNI(Java Native Interface) 或 JNA(Java Native Access)。由于现实中很多程序是由不同编程语言写的,必然会涉及到跨语言调用,比如 A 语言写的函数如果想在 B 语言里面调用,这时一般有两种解决方案:一种是将函数做成一个服务,通过进程间通信(IPC)或网络协议通信(RPC, RESTful等);另一种就是直接通过 FFI 调用。前者需要至少两个独立的进程才能实现,而后者直接将其它语言的接口内嵌到本语言中,所以调用效率比前者高。新手上路成长任务编辑入门编辑规则本人编辑我有疑问内容质疑在线客服官方贴吧意见反馈投诉建议举报不良信息未通过词条申诉投诉侵权信息封禁查询与解封©2024 Baidu 使用百度前必读 | 百科协议 | 隐私政策 | 百度百科合作平台 | 京ICP证030173号 京公网安备110000020000

PHP: 基础 FFI 用法 - Manual

PHP: 基础 FFI 用法 - Manual

Downloads

Documentation

Get Involved

Help

Getting Started

Introduction

A simple tutorial

Language Reference

Basic syntax

Types

Variables

Constants

Expressions

Operators

Control Structures

Functions

Classes and Objects

Namespaces

Enumerations

Errors

Exceptions

Fibers

Generators

Attributes

References Explained

Predefined Variables

Predefined Exceptions

Predefined Interfaces and Classes

Predefined Attributes

Context options and parameters

Supported Protocols and Wrappers

Security

Introduction

General considerations

Installed as CGI binary

Installed as an Apache module

Session Security

Filesystem Security

Database Security

Error Reporting

User Submitted Data

Hiding PHP

Keeping Current

Features

HTTP authentication with PHP

Cookies

Sessions

Dealing with XForms

Handling file uploads

Using remote files

Connection handling

Persistent Database Connections

Command line usage

Garbage Collection

DTrace Dynamic Tracing

Function Reference

Affecting PHP's Behaviour

Audio Formats Manipulation

Authentication Services

Command Line Specific Extensions

Compression and Archive Extensions

Cryptography Extensions

Database Extensions

Date and Time Related Extensions

File System Related Extensions

Human Language and Character Encoding Support

Image Processing and Generation

Mail Related Extensions

Mathematical Extensions

Non-Text MIME Output

Process Control Extensions

Other Basic Extensions

Other Services

Search Engine Extensions

Server Specific Extensions

Session Extensions

Text Processing

Variable and Type Related Extensions

Web Services

Windows Only Extensions

XML Manipulation

GUI Extensions

Keyboard Shortcuts?

This help

j

Next menu item

k

Previous menu item

g p

Previous man page

g n

Next man page

G

Scroll to bottom

g g

Scroll to top

g h

Goto homepage

g s

Goto search(current page)

/

Focus search box

PHP 回调 »

« 示例

PHP 手册 函数参考 影响 PHP 行为的扩展 FFI 示例

Change language:

English

German

Spanish

French

Italian

Japanese

Brazilian Portuguese

Russian

Turkish

Chinese (Simplified)

Other

Submit a Pull Request

Report a Bug

基础 FFI 用法

在深入了解 FFI API 细节之前,先看几个示例,展示 FFI API 在常规任务中的简单使用。

注意:

其中一些示例需要 libc.so.6,因此在没有该库的系统上无法运行。

示例 #1 从共享库中调用函数

printf("Hello %s!\n", "world");?>

以上示例会输出:

Hello world!

注意:

请注意,一些 C 函数需要特定的调用规则,例如 __fastcall、__stdcall 或 __vectorcall。

示例 #2 调用函数,通过参数返回结构体

new("struct timeval");$tz = $ffi->new("struct timezone");// 调用 C 的 gettimeofday()var_dump($ffi->gettimeofday(FFI::addr($tv), FFI::addr($tz)));// 访问 C 数据结构的字段var_dump($tv->tv_sec);// 打印完整数据结构var_dump($tz);?>

以上示例的输出类似于:

int(0)

int(1555946835)

object(FFI\CData:struct timezone)#3 (2) {

["tz_minuteswest"]=>

int(0)

["tz_dsttime"]=>

int(0)

}

示例 #3 访问已存在的 C 变量

errno);?>

以上示例会输出:

int(0)

示例 #4 创建和修改 C 变量

cdata);// 简单赋值$x->cdata = 5;var_dump($x->cdata);// 复合赋值$x->cdata += 2;var_dump($x->cdata);?>

以上示例会输出:

int(0)

int(5)

int(7)

示例 #5 使用 C 数组

以上示例会输出:

int(25)

int(523776)

int(1024)

int(8192)

示例 #6 使用 C 枚举

ZEND_FFI_SYM_TYPE);var_dump($a->ZEND_FFI_SYM_CONST);var_dump($a->ZEND_FFI_SYM_VAR);?>

以上示例会输出:

int(0)

int(2)

int(3)

+add a note

User Contributed Notes 1 note

up

down

5

wowabbs+php at gmail dot com ¶3 years ago

SystemParametersInfoA(SPI_SETDESKWALLPAPER, 0, $bmpFilePath, SPIF_UPDATEINIFILE || SPIF_SENDWININICHANGE); if ($Ret == 0) { $Error = $Kernel32->GetLastError(); throw new Exception("The call to the Windows API failed (error {$Error})."); } } $Url='https://www.php.net//images/news/phpkonf_2015.png'; $Img=File_Get_Contents($Url); File_Put_Contents($File=basename($Url), $Img); setWindowsDesktop(realpath($File));?>

+add a note

示例

基础 FFI 用法

PHP 回调

完整 PHP/FFI/preloading 示例

Copyright © 2001-2024 The PHP Group

My PHP.net

Contact

Other PHP.net sites

Privacy policy

使用 ffi/lib 对象 — CFFI 1.14.5 文档

使用 ffi/lib 对象 — CFFI 1.14.5 文档

CFFI

latest

目标

意见和错误

有什么新变化

安装和状态

概览

使用 ffi/lib 对象

使用指针,结构体和数组

Python 3支持

调用类似main的一个例子

函数调用

可变函数调用

内存压力 (PyPy)

外部 "Python" (新式回调)

外部 "Python" 和 void * 参数

从C语言直接访问外部 "Python"

外部 "Python+C"

外部 "Python": 参考

回调 (旧式)

Windows: 调用约定

FFI 接口

CFFI 参考

编写和分发模块

使用CFFI进行嵌入

CFFI

Docs »

使用 ffi/lib 对象

Edit on GitHub

使用 ffi/lib 对象¶

Contents

使用 ffi/lib 对象

使用指针,结构体和数组

Python 3支持

调用类似main的一个例子

函数调用

可变函数调用

内存压力 (PyPy)

外部 "Python" (新式回调)

外部 "Python" 和 void * 参数

从C语言直接访问外部 "Python"

外部 "Python+C"

外部 "Python": 参考

回调 (旧式)

Windows: 调用约定

FFI 接口

把这个页面放在枕边作为参考

使用指针,结构体和数组¶

C语言代码的整数和浮点值映射到Python的常规 int,long 和 float。 而且,C语言类型 char

对应于Python中的单字符字符串。 (如果要将其映射到小整数,请使用 signed char 或 unsigned char。)

同样,C语言类型 wchar_t 对应于单字符

unicode字符串。 请注意,在某些情况下(一个narrow的Python构建,具有底层的4字节wchar_t类型),单个wchar_t字符可能对应于一对代理(码元,码位),它们表示为长度为2的unicode字符串。 如果需要将这样的2-chars unicode字符串转换为整数,则 ord(x) 不起作用; 使用

int(ffi.cast('wchar_t', x)) 替换。

版本1.11中的新功能: 除了 wchar_t 之外,C语言类型

char16_t 和 char32_t 的工作方式相同,但具有已知的固定大小。

在以前的版本中,这可以使用 uint16_t 和

int32_t 实现,但不能自动转换为Python unicodes。

指针,结构和数组更复杂: 他们没有明显的Python等价物。 因此,它们对应类型 cdata 的对象,例如,它们被打印为

ffi.new(ctype, [initializer]): 此函数构建并返回给定 ctype 的新cdata对象。 ctype通常是一些描述C类型的常量字符串。 它必须是指针或数组类型。 如果是指针,例如 "int *" 或 struct foo *,然后它为一个 int 或 struct foo 分配内存。 如果是数组,例如 int[10],然后它为10个

int 分配内存。 在这两种情况下,返回的cdata都是 ctype 类型。

内存最初用零填充。 如下所述,也可以给出初始值。

例:

>>> ffi.new("int *")

>>> ffi.new("int[10]")

>>> ffi.new("char *") # allocates only one char---not a C string!

>>> ffi.new("char[]", "foobar") # this allocates a C string, ending in \0

与C不同,返回的指针对象对分配的内存具有 所有权: 当这个确切的对象被垃圾收集时,内存被释放。 如果在C语言级别,你在其他地方存储了一个指向内存的指针,接着确保你还可以根据需要保持对象存活。 (如果您立即将返回的指针强制转换为其他类型的指针,这也适用: 只有原始对象拥有所有权,所以你必须保持它的存活。 一旦忽略它,那么转换的指针将指向垃圾! 换句话说,所有权规则附加到包装器cdata对象: 它们不是也不能,附加到底层的原始内存中。)

例:

global_weakkeydict = weakref.WeakKeyDictionary()

def make_foo():

s1 = ffi.new("struct foo *")

fld1 = ffi.new("struct bar *")

fld2 = ffi.new("struct bar *")

s1.thefield1 = fld1

s1.thefield2 = fld2

# here the 'fld1' and 'fld2' object must not go away,

# otherwise 's1.thefield1/2' will point to garbage!

global_weakkeydict[s1] = (fld1, fld2)

# now 's1' keeps alive 'fld1' and 'fld2'. When 's1' goes

# away, then the weak dictionary entry will be removed.

return s1

通常你不需要弱字典: 例如,要使用包含指向 char * 指针的指针的 char * * 参数调用函数,这样就足够了:

p = ffi.new("char[]", "hello, world") # p is a 'char *'

q = ffi.new("char **", p) # q is a 'char **'

lib.myfunction(q)

# p is alive at least until here, so that's fine

然而,这总是错的 (使用释放的内存):

p = ffi.new("char **", ffi.new("char[]", "hello, world"))

# WRONG! as soon as p is built, the inner ffi.new() gets freed!

出于同样的原因,这也是错误的:

p = ffi.new("struct my_stuff")

p.foo = ffi.new("char[]", "hello, world")

# WRONG! as soon as p.foo is set, the ffi.new() gets freed!

cdata对象主要支持与C中相同的操作: 你可以从指针,数组和结构中读取或写入。 取消引用一个指针通常在C语言中语法为 *p,这不是有效的Python,所以你必须使用替代语法 p[0]

(这也是有效的C)。 另外,C语言中的 p.x 和 p->x 语法都在Python中成为 p.x。

我们有 ffi.NULL 同 C语言 NULL 在相同位置使用。

与后者类似,它实际上被定义为 ffi.cast("void *",

0)。 例如,读取一个NULL指针返回一个

NULL>,你可以检查, 例如通过与

ffi.NULL 比较。

C中的 & 运算符没有一般等价物 (因为它不适合模型,而且这里似乎不需要它)。 有 ffi.addressof(),但仅限于某些情况。 例如,你不能在Python中获取数字的 "地址"; 同样,

你不能获取CFFI指针的地址。 如果你有这种C语言代码:

int x, y;

fetch_size(&x, &y);

opaque_t *handle; // some opaque pointer

init_stuff(&handle); // initializes the variable 'handle'

more_stuff(handle); // pass the handle around to more functions

那么你需要像这样重写它,用逻辑上指向变量的指针替换C中的变量:

px = ffi.new("int *")

py = ffi.new("int *") arr = ffi.new("int[2]")

lib.fetch_size(px, py) -OR- lib.fetch_size(arr, arr + 1)

x = px[0] x = arr[0]

y = py[0] y = arr[1]

p_handle = ffi.new("opaque_t **")

lib.init_stuff(p_handle) # pass the pointer to the 'handle' pointer

handle = p_handle[0] # now we can read 'handle' out of 'p_handle'

lib.more_stuff(handle)

在C中返回指针或数组或结构类型的任何操作都会为您提供一个新的cdata对象。 与"原始"的方式不同,这些新的cdata对象没有所有权: 它们仅仅是对现有内存的引用。

作为上述规则的例外,取消引用一个拥有

struct 或 union 对象的指针会返回一个"共同拥有"相同内存的cdata struct 或 union 对象。 因此,在这种情况下,有两个对象可以保持相同的内存存活。 这样做是为了你真正想拥有一个struct对象但没有任何合适的位置来保存原始指针对象 (由

ffi.new() 返回)。

例:

# void somefunction(int *);

x = ffi.new("int *") # allocate one int, and return a pointer to it

x[0] = 42 # fill it

lib.somefunction(x) # call the C function

print x[0] # read the possibly-changed value

ffi.cast("type", value) 是C转换(casts)提供的等价物。

他们应该像在C中一样工作。 另外,这是获取整数或浮点类型的cdata对象的唯一方法:

>>> x = ffi.cast("int", 42)

>>> x

>>> int(x)

42

将指针强制转换为int,将它转换为 intptr_t 或 uintptr_t,

由C定义为足够大的整数类型 (例如32位):

>>> int(ffi.cast("intptr_t",pointer_cdata)) # signed

-1340782304

>>> int(ffi.cast("uintptr_t", pointer_cdata)) # unsigned

2954184992L

ffi.new()

的可选第二个参数给出的初始值可以是您用作C代码的初始值的任何东西,

使用列表或元组而不是使用C语法 { .., .., .. }。

例:

typedef struct { int x, y; } foo_t;

foo_t v = { 1, 2 }; // C syntax

v = ffi.new("foo_t *", [1, 2]) # CFFI equivalent

foo_t v = { .y=1, .x=2 }; // C99 syntax

v = ffi.new("foo_t *", {'y': 1, 'x': 2}) # CFFI equivalent

与C一样,字符数组也可以从字符串初始化,在这种情况下,隐式附加终止空字符:

>>> x = ffi.new("char[]", "hello")

>>> x

>>> len(x) # the actual size of the array

6

>>> x[5] # the last item in the array

'\x00'

>>> x[0] = 'H' # change the first item

>>> ffi.string(x) # interpret 'x' as a regular null-terminated string

'Hello'

同样,可以从unicode字符串初始化wchar_t或char16_t或char32_t的数组,并在cdata对象上调用 ffi.string() 返回存储在源数组中的当前unicode字符串 (必要时添加代理(码元,码位))。

有关更多详细信息,请参阅 Unicode字符类型 部分。

请注意,与Python列表或元组不同,但与C类似,你不能使用负数在最后的C数组中索引。

更一般地说,C语言数组类型的长度可以在C类型中未指定,只要它们的长度可以从初始化值得到,就像在C中:

int array[] = { 1, 2, 3, 4 }; // C syntax

array = ffi.new("int[]", [1, 2, 3, 4]) # CFFI equivalent

作为扩展,初始化值也可以只是一个数字,而且给出了长度 (如果你只想要零初始化):

int array[1000]; // C syntax

array = ffi.new("int[1000]") # CFFI 1st equivalent

array = ffi.new("int[]", 1000) # CFFI 2nd equivalent

如果长度实际上不是常数,这将非常有用,以避免像 ffi.new("int[%d]" % x) 这样的事情。 实际上,不建议这样做:

ffi 通常缓存字符串 "int[]" 不需要一直重新解析它。

C99支持可变大小结构,只要初始化值说明数组长度:

# typedef struct { int x; int y[]; } foo_t;

p = ffi.new("foo_t *", [5, [6, 7, 8]]) # length 3

p = ffi.new("foo_t *", [5, 3]) # length 3 with 0 in the array

p = ffi.new("foo_t *", {'y': 3}) # length 3 with 0 everywhere

最后,请注意,任何用作初始化值的Python对象也可以在没有 ffi.new() 的情况下直接用于数组项或结构字段的赋值。 实际上,p = ffi.new("T*", initializer) 是等价于 p = ffi.new("T*"); p[0] = initializer。 例:

# if 'p' is a

p[2] = [10, 20] # writes to p[2][0] and p[2][1]

# if 'p' is a , and foo_t has fields x, y and z

p[0] = {'x': 10, 'z': 20} # writes to p.x and p.z; p.y unmodified

# if, on the other hand, foo_t has a field 'char a[5]':

p.a = "abc" # writes 'a', 'b', 'c' and '\0'; p.a[4] unmodified

在函数调用中,传递参数时,这些规则也可以使用;

请参见 函数调用.

Python 3支持¶

支持Python 3,但要注意的要点是C语言

类型 char 对应Python类型 bytes,而不是 str。 在将所有Python字符串传递给CFFI或从CFFI接收它们时,您有责任将所有Python字符串编码/解码为字节。

这仅涉及 char 类型和派生类型; 在Python 2中接受字符串的API的其他部分继续接受Python 3中的字符串。

调用类似main的一个例子¶

想象一下,我们有某些类似这个:

from cffi import FFI

ffi = FFI()

ffi.cdef("""

int main_like(int argv, char *argv[]);

""")

lib = ffi.dlopen("some_library.so")

现在,一切都很简单,除了,我们如何在这里创建 char** 参数?

第一个想法:

lib.main_like(2, ["arg0", "arg1"])

不起作用,因为初始化值接收两个Python str 对象,

期望它是 对象。 您需要显式使用

ffi.new() 来创建这些对象:

lib.main_like(2, [ffi.new("char[]", "arg0"),

ffi.new("char[]", "arg1")])

请注意,两个 对象在调用期间保持活动状态: 它们仅在列表本身被释放时释放,并且仅在调用返回时释放列表。

如果你想要构建一个你想要重复使用的 "argv" 变量,那么需要更加小心:

# DOES NOT WORK!

argv = ffi.new("char *[]", [ffi.new("char[]", "arg0"),

ffi.new("char[]", "arg1")])

在上面的示例中,只要构建"argv",就会释放内部"arg0"字符串。 您必须确保保留对内部 "char[]" 对象的引用,直接或通过保持列表活动,像这样:

argv_keepalive = [ffi.new("char[]", "arg0"),

ffi.new("char[]", "arg1")]

argv = ffi.new("char *[]", argv_keepalive)

函数调用¶

调用C函数时,传递参数主要遵循与分配给结构字段相同的规则,返回值遵循与读取结构字段相同的规则。 例如:

# int foo(short a, int b);

n = lib.foo(2, 3) # returns a normal integer

lib.foo(40000, 3) # raises OverflowError

您可以将 char * 参数传递给普通的Python字符串 (但是不要将普通的Python字符串传递给带有 char *

参数的函数并且改变它!):

# size_t strlen(const char *);

assert lib.strlen("hello") == 5

您还可以将unicode字符串作为 wchar_t * 或 char16_t * 或

char32_t * 参数传递。 请注意C语言在使用 type * 或 type[] 的参数声明之间没有区别。 例如, int * 完全等价于 int[] (甚至 int[5]; 5被忽略了)。 对于CFFI, 这意味着您始终可以传递参数以转换为 int * 或 int[]。 例如:

# void do_something_with_array(int *array);

lib.do_something_with_array([1, 2, 3, 4, 5]) # works for int[]

请参见 参考: 转换 类似于传递 struct foo_s * 参数的方法————但一般来说,

在这种情况下传递

ffi.new('struct foo_s *', initializer) 更清晰。

CFFI支持传递和返回结构和联合到函数和回调。 例:

# struct foo_s { int a, b; };

# struct foo_s function_returning_a_struct(void);

myfoo = lib.function_returning_a_struct()

# `myfoo`:

出于性能,通过编写 lib.some_function 获得的非可变API级别级函数不是

对象, 而是不同类型的对象 (在CPython上,

function>)。 这意味着您不能将它们直接传递给其他C

语言函数期望的函数指针参数。 只有 ffi.typeof()

才能使用它们。 要获取包含常规函数指针的cdata,请使用 ffi.addressof(lib, "name")。

支持的参数和返回类型有一些(模糊的)限制。 这些限制来自libffi,仅适用于调用 函数指针; 换句话说,如果您使用API​​模式,它们不适用于不可变参数 cdef() 声明的函数。 限制是您不能直接作为参数传递或返回类型:

联合(union) (但是联合 指针 是不受限制的);

一个使用位字段的结构体(struct) (但这样的结构体 指针 是不受限制的);

在 cdef() 中用 "..." 声明的结构体。.

在API模式下,你可以解决这些限制: 例如,如果你需要从Python调用这样的函数指针,您可以改为编写一个自定义C语言函数,该函数接受函数指针和真实参数,并从C语言执行调用。 然后在 cdef() 中声明自定义C语言函数并从Python中使用它。

可变函数调用¶

C语言中的可变参数函数 (以 "..." 作为最后一个参数结束) 可以正常声明和调用,但可变部分中传递的所有参数必须是cdata对象。

这是因为无法猜测,如果你写了这个:

lib.printf("hello, %d\n", 42) # doesn't work!

你真的认为42作为C语言 int 传递,并不是

long 或 long long。 float 与

double 会出现同样的问题。 所以你必须强制你想要的C语言类型是cdata对象,

必要时使用 ffi.cast():

lib.printf("hello, %d\n", ffi.cast("int", 42))

lib.printf("hello, %ld\n", ffi.cast("long", 42))

lib.printf("hello, %f\n", ffi.cast("double", 42))

但理所当然:

lib.printf("hello, %s\n", ffi.new("char[]", "world"))

请注意,如果您使用的是 dlopen(),cdef() 中的函数声明必须与C中的原始声明完全匹配,像往常一样————尤其如此,如果此函数在C语言中是可变参数的,那么它的 cdef()

声明也必须是可变的。 您不能使用固定参数在

cdef() 中声明它,即使你打算只用这些参数类型调用它。 原因是某些体系结构具有不同的调用约定,具体取决于函数签名是否固定。 (在x86-64上,如果某些参数是 double 类型的,有时可以在PyPy的JIT生成的代码中看到差异。)

注意函数签名 int foo(); 由CFFI解释为等同于 int foo(void);。 这与C标准不同,

其中 int foo(); 真的像 int foo(...); 并且可以使用任何参数调用。 (这个特征是C89之前的遗留: 在不依赖于特定于编译器的扩展的情况下,在 foo() 的主体中根本无法访问参数。 现在几乎所有代码都使用 int foo(); 的真实意思是 int foo(void);。)

内存压力 (PyPy)¶

本段仅适用于PyPy,因为它的垃圾收集器(GC)与CPython不同。 在C语言代码中,通常有一对函数,一个执行内存分配或获取其他资源,而另一个让它们再次释放。 根据您构建Python代码的方式,只有在GC决定可以释放特定(Python)对象时才会调用释放函数。 这种情况尤其明显:

如果你使用 __del__() 方法来调用释放函数。

如果你使用 ffi.gc() 而不使用 ffi.release()。

如果在确定的时间调用释放函数,则不会发生这种情况,例如在常规 try: finally: 语句块。 然而它确实发生 在生成器内———— 如果生成器没有明确释放但忘记了 yield 这一点,则封闭在 finally 块中的代码仅在下一个GC处调用。

在这些情况下,您可能必须使用内置函数

__pypy__.add_memory_pressure(n)。 它的参数 n 是对要添加的内存压力的估量。 例如,如果这对C语言函数我们谈论的是 malloc(n) 和 free() 或类似的函数,则在 malloc(n) 之后调用 __pypy__.add_memory_pressure(n)。 这样做不总是问题的完整答案,

但它使下一个GC发生得更早,这通常就足够了。

如果内存分配是间接的,则同样适用,例如 C语言函数分配一些内部数据结构。 在这种情况下,使用参数 n 调用

__pypy__.add_memory_pressure(n) 这是一个粗略估量。 知道确切的大小并不重要,并且在调用释放函数后不必再次手动降低内存压力。 如果您正在为分配/释放函数编写包装器,你应该在前者中调用

__pypy__.add_memory_pressure(),即使用户可以在 finally: 块的已知点调用后者。.

如果这个解决方案还不够,或者,如果获取的资源不是内存,而是其他更有限的内容(如文件描述符),那么没有比重构代码更好的方法来确保在已知点调用释放函数而不是由GC间接调用。

请注意,在PyPy <= 5.6中,上面的讨论也适用于

ffi.new(). 在更新版本的PyPy中,both ffi.new() 和

ffi.new_allocator()() 都会自动解释它们创建的内存压力。 (如果您需要支持较旧和较新的PyPy,

无论如何,尝试调用 __pypy__.add_memory_pressure(); 最好扩大估量而不是考虑内存压力。)

外部 "Python" (新式回调)¶

当语言C代码需要一个指向函数的指针,该函数调用您选择的Python函数时,以下是在

out-of-line API模式下执行此操作的方法。

关于 回调 的下一节描述了ABI模式解决方案。

这是 1.4版本中的新功能。 如果向后兼容性存在问题,

请使用旧式 回调。 (原始回调调用较慢,并且与libffi的回调具有相同的问题; 值得注意的是,请看 警告 。 本节中描述的新样式根本不使用libffi的回调。)

在构建器脚本中,在cdef中声明一个以

extern "Python" 为前缀的函数:

ffibuilder.cdef("""

extern "Python" int my_callback(int, int);

void library_function(int(*callback)(int, int));

""")

ffibuilder.set_source("_my_example", r"""

#include

""")

然后,函数 my_callback() 在应用程序代码中的Python中实现:

from _my_example import ffi, lib

@ffi.def_extern()

def my_callback(x, y):

return 42

通过获取 lib.my_callback 获得 指针函数对象。 这个 可以传递给C代码, 然后像回调一样工作: 当C代码调用此函数指针时,调用Python函数 my_callback。 (您需要将 lib.my_callback 传递给C语言代码,而不是 my_callback: 后者只是上面的Python函数,不能传递给C语言。)

CFFI通过将 my_callback 定义为静态C语言函数来实现此功能,写在 set_source() 代码之后。

然后指向此功能。 这个函数的作用是调用Python函数对象,该函数在运行时附加

@ffi.def_extern()。

@ffi.def_extern() 装饰器应该应用于 全局函数, 一个用于同名的每个 extern "Python" 函数。

为了支持某些极端情况,可以通过再次调用 @ffi.def_extern() 来重新定义附加的Python函数,但是不建议这样做! 更好地为此名称附加单个全局Python函数,并首先灵活地写出来。 这是因为每个 extern "Python" 函数只能变成一个C语言函数。 调用 @ffi.def_extern() 会再次更改此函数的C逻辑以调用新的Python函数; 旧的Python函数不再可调用。 从 lib.my_function 获得的C语言函数指针始终是此C语言函数的地址,即 它保持不变。

外部 "Python" 和 void * 参数¶

如前所述,您不能使用 extern "Python" 来生成可变数量的语言C函数指针。 然而,在纯C语言代码中也无法实现该结果。 因此,C通常使用 void *data 参数定义回调。 您可以使用 ffi.new_handle() 和 ffi.from_handle() 通过 void * 参数传递Python对象。 例如,如果回调的C语言类型是:

typedef void (*event_cb_t)(event_t *evt, void *userdata);

并通过调用此函数来注册事件:

void event_cb_register(event_cb_t cb, void *userdata);

然后你会在构建脚本中编写这个:

ffibuilder.cdef("""

typedef ... event_t;

typedef void (*event_cb_t)(event_t *evt, void *userdata);

void event_cb_register(event_cb_t cb, void *userdata);

extern "Python" void my_event_callback(event_t *, void *);

""")

ffibuilder.set_source("_demo_cffi", r"""

#include

""")

并在您的主应用程序中注册这样的事件:

from _demo_cffi import ffi, lib

class Widget(object):

def __init__(self):

userdata = ffi.new_handle(self)

self._userdata = userdata # must keep this alive!

lib.event_cb_register(lib.my_event_callback, userdata)

def process_event(self, evt):

print "got event!"

@ffi.def_extern()

def my_event_callback(evt, userdata):

widget = ffi.from_handle(userdata)

widget.process_event(evt)

其他一些库没有明确的 void * 参数,但是允许您将 void * 附加到现有结构中。 例如,

库可能会说 widget->userdata 是为应用程序保留的通用字段。 如果事件的签名现在是这样的话:

typedef void (*event_cb_t)(widget_t *w, event_t *evt);

然后你可以使用低级 widget_t * 中的 void * 字段:

from _demo_cffi import ffi, lib

class Widget(object):

def __init__(self):

ll_widget = lib.new_widget(500, 500)

self.ll_widget = ll_widget #

userdata = ffi.new_handle(self)

self._userdata = userdata # must still keep this alive!

ll_widget.userdata = userdata # this makes a copy of the "void *"

lib.event_cb_register(ll_widget, lib.my_event_callback)

def process_event(self, evt):

print "got event!"

@ffi.def_extern()

def my_event_callback(ll_widget, evt):

widget = ffi.from_handle(ll_widget.userdata)

widget.process_event(evt)

从C语言直接访问外部 "Python"¶

如果你想直接从 set_source() 编写的C代码访问一些 extern "Python" 函数,你需要在之前写一个声明。 默认情况下,它必须是静态的,但请参阅 下一段 。) 在C代码之后由CFFI添加此函数的实际实现————这是必需的,因为声明可能使用由 set_source() 定义的类型 (例如 上面的 event_t 来自 #include),所以在此之前不能生成它。

ffibuilder.set_source("_demo_cffi", r"""

#include

static void my_event_callback(widget_t *, event_t *);

/* here you can write C code which uses '&my_event_callback' */

""")

这也可以用来编写直接调用Python的自定义C代码。 这是一个例子 (在这种情况下效率很低,但如果 my_algo() 中的逻辑要复杂得多,则可能会有用):

ffibuilder.cdef("""

extern "Python" int f(int);

int my_algo(int);

""")

ffibuilder.set_source("_example_cffi", r"""

static int f(int); /* the forward declaration */

static int my_algo(int n) {

int i, sum = 0;

for (i = 0; i < n; i++)

sum += f(i); /* call f() here */

return sum;

}

""")

外部 "Python+C"¶

使用 extern "Python" 声明的函数在C源中生成为 static 函数。 但是, 在某些情况下,将它们设置为非静态是很方便的,通常当您想要从其他C语言源文件直接调用它们时。 要做到这一点,你可以说 extern "Python+C" 而不只是 extern "Python"。 版本1.6中的新功能。

如果cdef包含

然后CFFI生成

extern "Python" int f(int);

static int f(int) { /* code */ }

extern "Python+C" int f(int);

int f(int) { /* code */ }

名称 extern "Python+C" 来自于我们想要两种意义上的外部函数: 作为一个 extern "Python",并且作为非静态的C语言函数。

你不能让CFFI生成额外的宏或其他特定于编译器的东西,比如GCC __attribute__。 您只能控制该功能是否应该是 static 的。 但通常,

这些属性必须与函数 头文件 一起写入,如果函数 实现 不重复它们就没问题:

ffibuilder.cdef("""

extern "Python+C" int f(int); /* not static */

""")

ffibuilder.set_source("_example_cffi", r"""

/* the forward declaration, setting a gcc attribute

(this line could also be in some .h file, to be included

both here and in the other C files of the project) */

int f(int) __attribute__((visibility("hidden")));

""")

外部 "Python": 参考¶

extern "Python" 必须出现在cdef()中。 就像C++ extern

"C" 语法一样,它也可以用于围绕一组函数的大括号:

extern "Python" {

int foo(int);

int bar(int);

}

The extern "Python" 函数现在不能是可变参数函数。 这可以在将来实施。 (这个 演示 展示了如何做到这一点,但它有点冗长。)

每个对应的Python回调函数都是使用

@ffi.def_extern() 装饰器定义的。 编写此函数时要小心: 如果它引发异常,或尝试返回错误类型的对象,那么异常无法传播。 而是将异常打印到stderr,并使C语言级回调返回默认值。 这可以通过 error 和

onerror 来控制,如下面所描述的。

@ffi.def_extern() 装饰器接受这些可选参数:

name: cdef中写入的函数的名称。 默认情况下,它取自您装饰的Python函数的名称。

error: 如果Python函数引发异常则返回一个值。 默认为0或null。 异常仍然打印到stderr,所以这应该只用作最后的解决方案。

onerror: 如果你想确保捕获所有异常,使用

@ffi.def_extern(onerror=my_handler)。 如果发生异常并指定了

onerror,然后调用 onerror(exception, exc_value,

traceback)。 这在某些情况下非常有用,在这种情况下,您不能简单地编写 try: except: 在主回调函数中,

因为它可能无法捕获信号处理程序引发的异常: 如果在C中发生信号,则尽快调用Python信号处理程序,这是在进入回调函数之后但在执行 try: 之前。 如果信号处理程序抛出,

我们还没有进入 try: except:。

如果调用 onerror 并正常返回,然后假设它自己处理异常并且没有任何内容打印到stderr。 如果 onerror 抛出,则会打印两个traceback信息。

最后,onerror 本身可以在C语言中提供回调的结果值,但不一定: 如果它只是返回 None————或者如果 onerror 本身失败————那么将使用 error 的值,如果有的话。

注意下面的技巧: 在 onerror 中,您可以按如下方式访问原始回调参数。 首先检查 traceback 是否为

None (它是 None 例如 如果整个函数成功运行但转换返回值时出错: 这在调用后发生). 如果 traceback 不是 None,

traceback.tb_frame 是最外层函数的框架,

即直接用

@ffi.def_extern() 装饰的函数的框架。 所以你可以通过阅读 traceback.tb_frame.f_locals['argname'] 获得该帧中 argname 的值。

回调 (旧式)¶

以下是如何创建一个包含函数指针的新 对象,该函数调用您选择的Python函数:

>>> @ffi.callback("int(int, int)")

>>> def myfunc(x, y):

... return x + y

...

>>> myfunc

>

请注意 "int(*)(int, int)" 是C 函数指针 类型,而

"int(int, int)" 是C 函数 类型。 两者都可以指定为

ffi.callback() 结果是相同的。

警告

为ABI模式提供回调或向后兼容。 如果你正在使用 out-of-line API 模式,建议使用 extern "Python" 机制而不是回调: 它提供更快,更清洁的代码。 它还避免了旧式回调的几个问题:

在不太常见的架构上,更容易在回调上崩溃 (例如在NetBSD上);

在 PAX and SELinux 等强化系统上,额外的内存保护可能会产生干扰 (例如,在 SELinux 上您需要在 deny_execmem 设置为 off 的情况下运行)。

On Mac OS X, 您需要为应用程序提供授权

com.apple.security.cs.allow-unsigned-executable-memory。

还要注意尝试针对此问题的cffi修复————请参阅 ffi_closure_alloc 分支————但没有合并,因为它使用 fork() 创建潜在的 memory corruption。

换一种说法: 是的,允许在程序中写入+执行内存是危险的; 这就是为什么存在上述各种"强化"选项的原因。 但与此同时,这些选择为另一次攻击打开了大门: 如果程序分支然后尝试调用任何 ffi.callback(),然后,这会立即导致崩溃————或者,攻击者只需要很少的工作就可以执行任意代码。 对我来说,它听起来比原来的问题更危险,这就是为什么cffi没有与他一起使用的原因。

要在受影响的平台上一劳永逸地解决问题,您需要重构所涉及的代码,以便它不再使用 ffi.callback().

警告: 与ffi.new()一样,ffi.callback()返回一个拥有其C语言数据所有权的cdata。 (在这种情况下,必要的C语言数据包含用于执行回调的libffi数据结构。) 这意味着只要此cdata对象处于活动状态,就只能调用回调。

如果将函数指针存储到C语言代码中,只要可以调用回调,就确保你也保持这个对象的存活。

最简单的方法是始终只在模块级使用 @ffi.callback(),并尽可能使用 ffi.new_handle() 传递 "context" 信息。 例:

# a good way to use this decorator is once at global level

@ffi.callback("int(int, void *)")

def my_global_callback(x, handle):

return ffi.from_handle(handle).some_method(x)

class Foo(object):

def __init__(self):

handle = ffi.new_handle(self)

self._handle = handle # must be kept alive

lib.register_stuff_with_callback_and_voidp_arg(my_global_callback, handle)

def some_method(self, x):

print "method called!"

(另请参阅上面关于 `外部 "Python"`_ 的部分,使用相同的一般风格。)

请注意,不支持可变参数函数类型的回调。 解决方法是添加自定义C代码。 在下面的示例中,回调获取第一个参数,该参数计算传递多少额外的 int

参数:

# file "example_build.py"

import cffi

ffibuilder = cffi.FFI()

ffibuilder.cdef("""

int (*python_callback)(int how_many, int *values);

void *const c_callback; /* pass this const ptr to C routines */

""")

ffibuilder.set_source("_example", r"""

#include

#include

static int (*python_callback)(int how_many, int *values);

static int c_callback(int how_many, ...) {

va_list ap;

/* collect the "..." arguments into the values[] array */

int i, *values = alloca(how_many * sizeof(int));

va_start(ap, how_many);

for (i=0; i

values[i] = va_arg(ap, int);

va_end(ap);

return python_callback(how_many, values);

}

""")

ffibuilder.compile(verbose=True)

# file "example.py"

from _example import ffi, lib

@ffi.callback("int(int, int *)")

def python_callback(how_many, values):

print ffi.unpack(values, how_many)

return 0

lib.python_callback = python_callback

弃用: 你也可以使用 ffi.callback() 作为装饰器而不是直接作为 ffi.callback("int(int, int)", myfunc)。 这是不鼓励的: 使用这个样式,我们更有可能在它仍在使用时过早忘记回调对象。

ffi.callback() 装饰器也接受可选参数

error,并从CFFI版本1.2接受可选参数 onerror。

这两个工作方式与 上面描述的 "外部 Python" 相同。

Windows: 调用约定¶

在Win32上,函数可以有两个主要的调用约定: "cdecl" (默认),或 "stdcall" (也被称为 "WINAPI")。 还有其他罕见的调用约定,但这些都不受支持。

版本1.3中的新功能。

当您从Python发出调用到C时,实现是这样的,它适用于这两个主要调用约定中的任何一个; 你不必指定它。 但是,如果您操作"函数指针"类型的变量或声明回调,则调用约定必须正确。 这是通过在类型中写入 __cdecl 或 __stdcall

来完成的,就像在C中一样:

@ffi.callback("int __stdcall(int, int)")

def AddNumbers(x, y):

return x + y

或:

ffibuilder.cdef("""

struct foo_s {

int (__stdcall *MyFuncPtr)(int, int);

};

""")

支持 __cdecl 但始终是默认值,因此可以省略它。 在 cdef() 中,您还可以使用 WINAPI 作为

__stdcall 的等效项。 如上所述,它几乎不需要 (但不会受到影响) 在 cdef() 中声明一个普通函数时说明 WINAPI 或 __stdcall。 (如果使用 ffi.addressof() 显式指向此函数的指针,或者函数是 extern "Python",仍然可以看到差异。)

这些调用约定说明符被接受但在32位Windows以外的任何平台上都被忽略。

在1.3之前的CFFI版本中,调用约定说明符无法识别。 在API模式下,您可以通过使用间接来解决它,就像关于 回调

("example_build.py") 一节中的示例一样。 在ABI模式下无法使用stdcall回调。

FFI 接口¶

(FFI接口的参考已移至 下一页.)

Next

Previous

© Copyright 2012-2018, Armin Rigo, Maciej Fijalkowski, Jairo(翻译者)

Revision d086af7d.

Built with Sphinx using a theme provided by Read the Docs.

Read the Docs

v: latest

Versions

latest

1.14

1.13

1.12

Downloads

pdf

html

epub

On Read the Docs

Project Home

Builds

Free document hosting provided by Read the Docs.

使用 dart:ffi 与 C 进行交互 | Dart

使用 dart:ffi 与 C 进行交互 | Dart

Google uses cookies to deliver its services, to personalize ads, and to

analyze traffic. You can adjust your privacy controls anytime in your

Google settings.

Learn more.

Okay

menu

概览

文档

社区

尝试 Dart

获取 Dart SDK

Dart 3.1 已经发布!

概览

社区

尝试 Dart

获取 Dart SDK

文档

样例和教程

语言概览

Codelabs

Codelabs 列表

Dart 速查表

可迭代集合

Dart 异步编程

空安全

Dart 开发语言

介绍

基础表达式

变量

操作符

注释

注解

库 & 导库

关键字

类型

基本类型

记录 (Records)

集合 (Collections)

泛型 (Generics)

别名 (Typedefs)

类型系统

模式匹配

概览 & 用法

模式匹配类型

函数方法

控制流

循环

分支

错误处理

类 & 对象

构造方法

成员方法

继承

混入 (Mixin)

枚举

扩展方法

可调用的对象

类型修饰符

概览 & 用法

API 维护者应该用的类型修饰符

速查表

并发

异步支持

Isolates

Null safety

健全的空安全

迁移到空安全

深入理解空安全

非健全的空安全

空安全常见问题和解答

高效指南 (Effective Dart)

概述

代码风格

文档

用法示例

API 设计

核心库

概览

概览

介绍文章

创建 stream

Packages

如何使用 package

常用 package 介绍

创建 package

发布 package

设置 package 介绍页

Package 参考资料

依赖

术语表

Package 文件结构

设置环境变量

Pubspec 文件

问题排查

发布者认证

版本管理

开发文档

Futures、async 和 await

Streams 介绍

使用 JSON

Dart 中的数字

与其他语言进行交互调用

与 C 互调

与 Objective-C 和 Swift 互调

与 Java 和 Kotlin 互调

与 JavaScript 互调

Google APIs

跨平台应用

命令行和服务端应用

概览

起步教程

命令行应用

从网络上获取数据

编写 HTTP 服务端应用

库和 package

Google Cloud

网页端 (Web) 应用

概览

开始使用

底层 Web 编程

Dart 与 HTML 关联

向 DOM 添加元素

移除 DOM 元素

部署

常用 Web 库和 package

环境变量声明

开发工具和使用技巧

概览

编辑器和调试工具

使用 IntelliJ 和 Android Studio

使用 VS Code

Dart 开发者工具

在线运行 Dart 代码

Overview

DartPad 教程

DartPad 疑难解答

命令行工具

Dart SDK

概览

dart 命令

dart analyze 命令

dart compile 命令

dart create 命令

dart doc 命令

dart fix 命令

dart format 命令

dart info 命令

dart pub 命令

dart run 命令

dart test 命令

dartaotruntime 命令

实验性命令标记

其他的命令行工具

build_runner 命令

webdev 命令

源码管理

代码格式化

不应提交的内容

静态分析

自定义静态分析

修复常见的类型问题

诊断消息

Linter 规则

测试和调优

测试

调试 Web 应用

资源

常见问题

破坏性改动

版本演变

语言规范

Dart 3 迁移指南

从其他平台转向 Dart

从 JavaScript 到 Dart

从 Swift 到 Dart

词汇表

书籍资源

视频资源

相关站点

API 文档

open_in_new

Dart 团队官方博客

open_in_new

DartPad (在线编辑器)

open_in_new

Flutter

open_in_new

Package 网站

open_in_new

关于 Dart 中文文档

关于本站

open_in_new

免责条款 (Disclaimer)

open_in_new

目录

示例

快速上手的 hello_world

文件

构建并运行

使用 dart:ffi

集成并加载 C 库

Interfacing with native types

使用 package:ffigen 生成 FFI 的绑定

description

bug_report

使用 dart:ffi 与 C 进行交互

目录

keyboard_arrow_down

keyboard_arrow_up

示例

快速上手的 hello_world

文件

构建并运行

使用 dart:ffi

集成并加载 C 库

Interfacing with native types

使用 package:ffigen 生成 FFI 的绑定

more_horiz

Dart 的移动端、命令行和服务端应用所运行的 Dart 原生平台,均可以使用 dart:ffi 库调用原生的 C 语言 API,用于读、写、分配和销毁原生内存。

FFI 指的是 外部函数接口。类似的术语包括 原生接口 和 语言绑定。

相关的 API 文档可在

dart:ffi API 文档

查看。

示例

以下的示例将展示如何使用 dart:ffi 库:

示例

描述

hello_world

如何调用无参数和返回值的 C 语言函数。

primitives

如何调用参数和返回值为 整型和指针 的 C 语言函数。同时演示 varargs。

structs

如何与 C 语言互相传递字符串,以及如何处理 C 语言定义的结构。

sqlite

Dart SDK 仓库中包含的 小型示例。

快速上手的 hello_world

hello_world 示例 展示了如何用最少的代码调用 C 语言库。

文件

hello_world 示例包含了以下文件:

源文件

描述

hello.dart

使用了 C 语言库中的 hello_world() 函数的文件。

pubspec.yaml

Dart 里常见的 pubspec 文件,最低 SDK 限制为 2.6。

hello_library/hello.h

声明了 hello_world() 函数。

hello_library/hello.c

该 C 文件导入了 hello.h 并实现了 hello_world() 函数。

hello_library/hello.def

包含 DLL 构建信息的模块定义。

hello_library/CMakeLists.txt

将 C 文件代码编译为动态库的 CMake 文件。

构建 C 代码库时将创建几个文件,包括动态库

libhello.dylib(仅 macOS)、

libhello.dll(仅 Windows)或 libhello.so(仅 Linux)。

构建并运行

以下是构建动态库并执行 Dart 应用的示例:

$ cd hello_library

$ cmake .

...

$ make

...

$ cd ..

$ dart pub get

$ dart run hello.dart

Hello World

info

在 macOS 上, 包括 Dart VM (dart)

在内的可执行文件只能加载 已签名的库。

若想了解更多细节和解决方案,请查看 签名指南。

使用 dart:ffi

hello.dart 文件

阐述了使用 dart:ffi 调用 C 函数的步骤:

导入 dart:ffi。

导入 path 库用于合成动态库的路径。

为 C 函数的 FFI 类型签名的定义一个类型。

为调用 C 函数的变量定义一个类型。

利用一个变量保存动态库的路径。

加载包含 C 函数的动态库。

创建该 C 函数的引用,接着将其赋予变量。

调用 C 函数。

以下是每一个步骤对应的代码。

导入 dart:ffi。

import 'dart:ffi' as ffi;

导入 path 库用于合成动态库的路径。

import 'dart:io' show Platform, Directory;

import 'package:path/path.dart' as path;

为 C 函数的 FFI 类型签名的定义一个类型。

参阅 定义原生类型的接口

了解 dart:ffi 库中定义的常用类型。

typedef hello_world_func = ffi.Void Function();

为调用 C 函数的变量定义一个类型。

typedef HelloWorld = void Function();

利用一个变量保存动态库的路径。

var libraryPath = path.join(Directory.current.path, 'hello_library',

'libhello.so');

if (Platform.isMacOS) {

libraryPath = path.join(Directory.current.path, 'hello_library',

'libhello.dylib');

} else if (Platform.isWindows) {

libraryPath = path.join(Directory.current.path, 'hello_library',

'Debug', 'hello.dll');

}

加载包含 C 函数的动态库。

final dylib = ffi.DynamicLibrary.open(libraryPath);

创建该 C 函数的引用,接着将其赋予变量。这段代码使用了步骤 2 和 3 定义的类型,以及步骤 4 创建的动态库变量。

final HelloWorld hello = dylib

.lookup>('hello_world')

.asFunction();

调用 C 函数。

hello();

当你理解 hello_world 示例的内容后,可以进一步学习 其他的 dart:ffi 示例。

集成并加载 C 库

根据平台和库的类型的不同,捆绑(或 打包 和 分发)

C 库到 package 或应用并进行加载的方式,有所不同。

Flutter 的 dart:ffi 页面: Android、iOS 和 macOS

dart:ffi 示例

Interfacing with native types

The dart:ffi library provides multiple types

that implement NativeType

and represent native types in C.

Some native types are only used as markers in type signatures

while others (or their subtypes) can be instantiated.

Instantiable native types

The following native types can be used as markers in type signatures

and they (or their subtypes) can be instantiated in Dart code:

Dart type

Description

Array

A fixed-sized array of items. Supertype of type specific arrays.

Pointer

Represents a pointer into native C memory.

Struct

The supertype of all FFI struct types.

Union

The supertype of all FFI union types.

Purely marker native types

The following are platform-agnostic native types

that are used only as markers in type signatures,

and can’t be instantiated in Dart code:

Dart type

Description

Bool

Represents a native bool in C.

Double

Represents a native 64 bit double in C.

Float

Represents a native 32 bit float in C.

Int8

Represents a native signed 8 bit integer in C.

Int16

Represents a native signed 16 bit integer in C.

Int32

Represents a native signed 32 bit integer in C.

Int64

Represents a native signed 64 bit integer in C.

NativeFunction

Represents a function type in C.

Opaque

The supertype of all opaque types in C.

Uint8

Represents a native unsigned 8 bit integer in C.

Uint16

Represents a native unsigned 16 bit integer in C.

Uint32

Represents a native unsigned 32 bit integer in C.

Uint64

Represents a native unsigned 64 bit integer in C.

Void

Represents the void type in C.

There are also many ABI specific marker native types

that extend AbiSpecificInteger.

Refer to their linked API documentation for more information and

a guideline on what types they map to on specific platforms:

Dart type

Description

AbiSpecificInteger

The supertype of all ABI-specific integer types.

Int

Represents the int type in C.

IntPtr

Represents the intptr_t type in C.

Long

Represents the long int (long) type in C.

LongLong

Represents the long long type in C.

Short

Represents the short type in C.

SignedChar

Represents the signed char type in C.

Size

Represents the size_t type in C.

UintPtr

Represents the uintptr_t type in C.

UnsignedChar

Represents the unsigned char type in C.

UnsignedInt

Represents the unsigned int type in C.

UnsignedLong

Represents the unsigned long int (unsigned long) type in C.

UnsignedLongLong

Represents the unsigned long long type in C.

UnsignedShort

Represents the unsigned short type in C.

WChar

Represents the wchar_t type in C.

使用 package:ffigen 生成 FFI 的绑定

为大量的 API 编写绑定可能要花费你的大量时间。你可以使用 package:ffigen

绑定生成器,自动地从 C 头文件生成 FFI 包装,从而减少时间消耗。

引用中文内容需注明本站及链接作为出处,英文内容和示例代码均遵从源站授权协议。

使用条款

隐私政策

Dart 代码安全说明

关于中文文档

免责条款

京ICP备13029451号-6 | 京公网安备11010802029624号