Web Analytics

coffi

⭐ 357 stars Simplified Chinese by IGJoshua

coffi

Clojars Project cljdoc badge

Coffi 是一个用于 Clojure 的外部函数接口库,使用 JDK 22 及更高版本中的 Foreign Function & Memory API。这允许直接从 Clojure 调用本地代码, 而无需像 JNI 那样依赖于库特定的 Java 或本地代码。Coffi 侧重于易用性,包括用于创建包装器的函数和宏, 使得生成的本地函数表现得就像 Clojure 函数一样,但这并不妨碍编写系统以最小化数据编组成本并优化性能, 利用 FF&M API 提供的低级访问能力。

安装

此库可在 Clojars 上获得,也可以作为 git 依赖。将以下条目之一添加到你的 deps.edn 文件的 :deps 键中:

org.suskalo/coffi {:mvn/version "1.0.615"}
io.github.IGJoshua/coffi {:git/tag "v1.0.615" :git/sha "7401485"}
如果您将此库作为 git 依赖使用,您需要准备该库。

$ clj -X:deps prep
Coffi 需要使用包 java.lang.foreign,并且大多数操作被 JDK 视为不安全,因此在不传递某些命令行标志的情况下,您的代码无法使用这些操作。为了使用 coffi,请向您的应用程序添加以下 JVM 参数。

--enable-native-access=ALL-UNNAMED

您可以在特定的 Clojure CLI 调用中使用 -J 标志指定 JVM 参数,方法如下:

`` sh clj -J--enable-native-access=ALL-UNNAMED


你也可以在你的 deps.edn 文件中的别名下通过
:jvm-opts 键指定它们(见下一个示例),然后使用 -M-A-X
调用带有该别名的 CLI。
clojure {:aliases {:dev {:jvm-opts ["--enable-native-access=ALL-UNNAMED"]}}}
其他构建工具如果查阅它们的文档,也应提供类似的功能。

在创建可执行的 jar 文件时,可以通过向 jar 添加清单属性 Enable-Native-Access: ALL-UNNAMED 来避免传递此参数的需求。有关如何添加,请参阅您的构建工具文档。

Coffi 还支持 linter clj-kondo。如果您使用 clj-kondo 并且该库的宏未正确进行 lint,您可能需要安装随库捆绑的配置。您可以通过以下 shell 命令完成,需在项目目录下运行:

sh $ clj-kondo --copy-configs --dependencies --lint "$(clojure -Spath)"

使用方法

两个主要的命名空间是
coffi.mem,它提供分配和操作堆外内存以及(反)序列化值的函数,和 coffi.ffi,它可以加载本地库,声明本地函数包装器,以及将函数作为回调进行(反)序列化。

clojure (require '[coffi.mem :as mem]) (require '[coffi.ffi :as ffi :refer [defcfn]])

(defcfn strlen "Given a string, measures its length in bytes." strlen [::mem/c-string] ::mem/long)

(strlen "hello") ;; => 5

(ffi/load-system-library "z") `coffi.mem 命名空间中,包含了所有 C 语言中有符号原始数值类型的类型,此外还有 ::mem/pointer::mem/c-string,以及使用类似 malli 的类型声明来定义结构体、联合体、数组、枚举和标志集的方法。

替代方案

该库并不是唯一提供访问本地代码的 Clojure 库。此外,还存在以下(以及其他)库:

Dtype-next 支持 Java 版本 8-15、17+ 以及 GraalVM,但重点是基于数组的编程,并且侧重于将内存保持在本地端,而不是在 Clojure 本地结构和 C 之间来回传递数据。在 Java 17+ 中,它使用外部函数与内存 API(Project Panama 的一部分,直到 JDK 22 稳定),而在其他 Java 版本中使用 JNA。

Tech.jna 和 clojure-jna 在所有情况下都使用 JNA 库,并且都不提供对回调的显式支持。JNA 允许使用 java.nio.ByteBuffer 来按值传递结构体,这两个库都提供了使用这种按值构造来调用按引用 API 的方式。

coffi 的另一个替代方案是直接使用 JNI,这是 JVM 中封装本地代码最久远的方法,但缺点是你需要编写本地代码和 Java 代码,即使你只打算从 Clojure 中使用它。

如果你的应用需要能够运行在早于 JVM 22 的版本中,应考虑这些其他选项。Dtype-next 提供了最强大的本地代码支持,但如果你只是封装一个简单的库,其他库可能更具吸引力,因为它们的 API 面较小,且更容易封装函数。

此外,还有一个 第三方 FFI 选项汇总 列出了 Clojure 的 FFI 选项。

已知问题

项目作者已知晓这些问题,并计划在未来的版本中修复它们:

  • 当在依赖 coffi 的库中使用 codox 生成文档时,会出现以下错误。临时的解决方法是在你的 codox 构建中显式依赖版本为 0.2.1 的 insn。
` Unable to find static field: ACC_OPEN in interface org.objectweb.asm.Opcodes ``

未来计划

这些功能计划在未来版本中实现。

许可证

版权所有 © 2023 Joshua Suskalo

根据 Eclipse 公共许可证版本 1.0 发布。

--- Tranlated By Open Ai Tx | Last indexed: 2025-11-30 ---