User:Pdeantihuman/Foreign function interface
外部函数接口( FFI ) 是一种机制,用一种编程语言编写的程序可以通过这种机制调用其他编程语言编写的例程或服务。
命名
[编辑]这个术语来自Common Lisp的规范,它明确地指代语言间调用的语言特性[1];该术语也被Haskell[2]、Rust[3]、和Python编程语言[4]正式使用。其他语言使用其他术语: Ada 编程语言将其称为“语言绑定”,而Java将其 FFI 称为 JNI( Java 本机接口)或 JNA( Java 本机访问)。外部函数接口已成为提供此类服务的机制的通用术语。
操作
[编辑]外部函数接口的主要功能是将一种编程语言(宿主语言或定义 FFI 的语言)的语义调用约定与另一种(客户语言)的语义和约定相匹配。此过程还必须考虑两者的运行时环境或应用程序二进制接口。这可以通过多种方式完成:
- 要求以特定方式指定或实现可被宿主语言调用的来宾语言函数,通常使用某种兼容性库。
- 使用工具自动“包装”带有适当胶水代码的客户语言功能,执行任何必要的翻译。
- 包装库的使用
- 限制可以跨语言使用的宿主语言功能集。例如,从 C 调用的 C++ 函数(通常)可能不包含引用参数或抛出异常。
FFI 可能会因以下考虑而变得复杂:
- 如果一种语言支持垃圾收集(GC) 而另一种语言不支持;必须注意非 GC 语言代码不会导致其他语言的 GC 失败。例如,在 JNI 中,“保留”从 Java 接收到的对象引用的 C 代码必须向Java 运行时环境(JRE)“注册”这一事实;否则,Java 可能会在 C 完成之前删除对象。 (一旦 C 不再需要该对象,C 代码还必须显式释放其与任何此类对象的链接。 )
- 复杂或非平凡的对象或数据类型可能难以从一种环境映射到另一种环境。
- 由于上述映射问题,两种语言可能无法维护对同一可变对象实例的引用。
- 一种或两种语言可能在虚拟机(VM) 上运行;此外,如果两者都是,则它们可能是不同的 VM。
- 跨语言继承和其他差异,例如类型系统之间或对象组合模型之间的差异,可能特别困难。
在不同语言中
[编辑]FFI 的例子包括:
- Ada 语言绑定,不仅允许调用外部函数,还允许导出其函数和方法以从非 Ada 代码中调用。 [5]
- C++ 与 C 有一个微不足道的 FFI,因为这些语言共享一个重要的公共子集。
extern "C"
声明的主要作用是禁用 C++ 名字修饰 。 - Clean 为遵循 C 或 stdcall 调用约定的所有语言提供双向 FFI。 [6] [7]
- CNI ,替代在 GNU 编译器环境中使用的 JNI。
- D 的处理方式与C++相同,使用
extern "C"
和extern(C)
- Dart包括 dart:ffi [8]库,用于为移动、命令行和服务器应用程序调用本机 C 代码
- 动态语言,例如Python 、 Perl 、 Tcl和Ruby ,都可以轻松访问用 C/C++(或任何其他遵循 C/C++ 调用约定的语言)编写的本机代码。
- Factor具有 C、 Fortran 、 Objective-C和Windows COM 的FFI;所有这些都支持动态导入和调用任意共享库。
- Common Lisp和Haskell的 FFI
- Fortran 2003 有一个模块 ISO_C_BINDING,它提供可互操作的数据类型(内部类型和 POD 结构)、可互操作的指针、可互操作的全局数据存储,以及从 Fortran 调用 C 和从 C 调用 Fortran 的机制。 [9]
- Go 可通过
"C"
伪包直接调用 C 代码。 [10] - 在GWT中,Java 被编译为 JavaScript,有一个名为 JSNI 的 FFI,它允许 Java 源调用任意 JavaScript 函数,并让 JavaScript 回调到 Java。
- JNI ,它提供Java和 C/C++ 之间的接口,C/C++ 是大多数部署 Java 的系统上的首选系统语言。 JNA提供了与本机库的接口,而无需编写粘合代码。另一个例子是JNR
- Nim有一个 FFI,使它能够使用来自C 、 C++和Objective-C 的源代码。它还可以与 Javascript 接口。
- Julia有
ccall
关键字来调用 C(和其他语言,例如Fortran); [11]虽然提供类似无样板支持的包可用于某些语言,例如 Python [12] (例如提供 OO 支持和 GC 支持)、Java(并支持其他 JDK 语言,例如 Scala)和 R . Cxx.jl 包也可以与 C++ 交互使用。 - PHP 为 C 提供 FFI。 [13]
- Ruby 通过 ffi gem 或标准库fiddle提供 FFI。
require 'fiddle'
libm = Fiddle.dlopen('/lib/libm.so.6')
# 等价于: double floor(double x);
floor = Fiddle::Function.new(
libm.sym('floor'), # ptr 是一个函数的引用
[Fiddle::TYPE_DOUBLE], # args 是一个参数数组,传递给ptr函数
Fiddle::TYPE_DOUBLE # ret_type 是函数的返回值
)
# Equivalent to: floor(3.14159);
floor.call(3.14159) #=> 3.0
- Python提供了ctypes和cffi模块。例如,ctypes 模块可以即时从共享库/ DLL加载 C 函数,并在 Python 和 C 语义之间自动转换简单的数据类型,如下所示:
import ctypes libc = ctypes.CDLL('/lib/libc.so.6') # Under Linux/Unix t = libc.time(None) # 等价于 C 代码: t = time(NULL) print(t)
- P/Invoke ,它提供了 Microsoft公共语言运行时和本机代码之间的接口。
- Racket有一个基于宏的原生 FFI,可以动态导入任意共享库。 [14] [15]
- Raku可以调用Ruby, Python, Perl, Brainfuck, Lua, C, C++, Go和 Scheme Guile / Gambit [16] [17]
- Rust还定义了一个外部函数接口。 [18]
- Visual Basic具有声明性语法,允许它调用非 Unicode C 函数。
- 组件对象模型的基础之一是通用接口格式,它本机使用与 Visual Basic 相同的类型来处理字符串和数组。
- LuaJIT 是Lua的JIT实现,具有允许“从纯 Lua 代码调用外部 C 函数和使用 C 数据结构”的 FFI。 [19]
- PhoneGap (以前叫 Apache Callback,现在叫 Apache Cordova)是一个使用 HTML、CSS 和 JavaScript 构建原生移动应用程序的平台。此外还有通过 JavaScript 回调函数的 FFI,用于访问手机原生功能的方法和属性,包括加速度计、相机(还有 PhotoLibrary 和 SavedPhotoAlbum)、指南针、存储(SQL 数据库和 localStorage)、通知、媒体和捕获(播放和录制或音频)和视频)、文件、联系人(地址簿)、事件、设备和连接信息。 [1], [2] 。
- Wolfram 语言提供了一种称为 WSTP(Wolfram 符号传输协议)的技术,该技术支持在其他语言之间双向调用代码并绑定 C++、Java、. NET 和其他语言。
- Zig
cImport
函数为 c 提供 FFI。 [20]
此外,许多 FFI 可以自动生成:例如SWIG 。然而,在扩展语言的情况下,客机和主机关系的语义反转可能发生,当较小的扩展语言主体是客机调用较大主体语言主体中的服务时,例如编写一个小插件[21]用于 GIMP。 [22]
一些 FFI 仅限于独立函数,而另一些 FFI 也允许调用嵌入在对象或类中的函数(通常称为方法调用);有些甚至允许跨语言边界迁移复杂的数据类型和/或对象。
在大多数情况下,FFI 是由“高级”语言定义的,因此它可以使用以低级语言(通常是C或C++等系统语言)定义和实现的服务。这样做通常是为了以定义操作系统 API 的语言访问操作系统服务,或者出于性能考虑。
许多 FFI 还为被调用语言提供调用宿主语言服务的方法。
术语外部函数接口通常不用于描述多语言运行时,例如 Microsoft公共语言运行时,其中提供了一个公共“基板”,使任何符合 CLR 的语言能够使用任何其他语言中定义的服务。 (但是,在这种情况下,CLR 确实包含 FFI P/Invoke以在运行时外部调用。 ) 此外,Java 远程方法调用(RMI)、RPC、 CORBA 、 SOAP和D-Bus等许多分布式计算架构允许用不同的语言编写不同的服务;此类架构通常不被视为 FFI。
特别案例
[编辑]有一些特殊情况,其中语言编译成相同的字节码 VM,如Clojure和Java ,以及Elixir和Erlang 。由于没有接口,严格来说它不是 FFI,但它为用户提供了相同的功能。
参见
[编辑]参考文献
[编辑]- ^ CFFI User Manual. common-lisp.org. [2015-06-18].
- ^ FFI Introduction. HaskellWiki. [19 June 2015].
Haskell's FFI is used to call functions from other languages (basically C at this point), and for C to call Haskell functions.
- ^ std::ffi - Rust. [1 April 2021].
This module provides utilities to handle data across non-Rust interfaces, like other programming languages and the underlying operating system. It is mainly of use for FFI (Foreign Function Interface) bindings and code that needs to exchange C-like strings with other languages.
- ^ CFFI documentation. [19 June 2015].
C Foreign Function Interface for Python. The goal is to provide a convenient and reliable way to call compiled C code from Python using interface declarations written in C.
- ^ Interface to Other Languages. Adaic.org. [2013-09-29].
- ^ Foreign Export. [2020-05-25].
- ^ Calling C From Clean. [2018-04-25].
- ^ dart:ffi library. [2020-01-01].
- ^ 'fortran-iso-c-binding' tag wiki. Stack Overflow.
- ^ cgo — The Go Programming Language. [2015-08-23].
- ^ Calling C and Fortran Code · The Julia Language. docs.julialang.org. [2018-02-11] (英语).
- ^ PyCall.jl: Package to call Python functions from the Julia language, JuliaPy, 2018-02-08 [2018-02-11]
- ^ PHP: FFI - Manual. The PHP Group. [13 June 2019].
- ^ Eli Barzilay. The Racket Foreign Interface. Docs.racket-lang.org. [2013-09-29].
- ^ TR600.pdf (PDF). [2013-09-29].
- ^ Inline implementations. [2017-08-15].
- ^ Native Call. [2017-08-15].
- ^ Using extern Functions to Call External Code. [2019-06-01].
- ^ Mike Pall. FFI Library. Luajit.org. [2013-09-29].
- ^ Import from C Header File. Zig Software Foundation. [2021-03-11].
- ^ 4. A sample script. Gimp.org. 2001-02-04 [2013-09-29].
- ^ Script-Fu and plug-ins for The GIMP. Gimp.org. [2013-09-29].