Squeak 中的 FFI:Singleton ExternalLibrary 与 class 方法与 ExternalStructures 中的方法?

FFI in Squeak: Singleton ExternalLibrary vs. class methods vs. methods in ExternalStructures?

我正在为现有库(用 C 编写)编写 FFI 接口。

该库使用了大量不透明结构,因此我定义了一些 ExternalStructures(没有字段)用作 void*

现在我看到了两种(或四种?)与库交互的方式:

有一个 ExternalLibrary 每个导出函数都有一个方法:这可以在实例 class 中有方法,然后使用单例模式来拥有一个实例。或者使用 "more complex" 语法在 class 端实现方法,包括 FFI 编译指示中的 moduleName,如:

ffiTestFloats: f1 with: f2
  "FFITestLibrary ffiTestFloats: $A with: 65.0"
  <cdecl: float 'ffiTestFloats' (float float) module:'SqueakFFIPrims'>
  ^self externalCallFailed

哪个更好?

此外,我还看到了其他方法,根本没有 ExternalLibrary,而是直接在 ExternalStructure 中实现这些方法。我更喜欢第二部分,但是,所有 FFI 接口定义都分布在多个 classes 中,维护和移植到其他平台、Smalltalk 方言或库版本可能会更复杂。

那么,"right" 方法是什么?

我更喜欢第一个选项,一个表示外部库的对象,在 C 函数和实例方法之间具有一对一的映射。当然,这是较低级别的抽象,应该在其之上构建更好的抽象。 我不会使用 "singleton",我认为不需要 "one instance of the class"。您需要的是一个众所周知的对象,并且不应从内部直接引用该对象以避免耦合,应该在需要时将其作为参数传递。

我会坚持传统的建模方法。我们这里有一个外部库,然后让我们为它创建一个 class 并在我们的对象中复制它的 API,当然,使用执行所需 FFI 调用的实例端方法。

我们已经使用这种方法二十年了,经验表明单例模式在这种情况下效果很好,因为它使客户的生活变得轻松。当然,必须小心处理此工具,以免您从不适当的地方引用库实例。但是请注意,这不是一个必要的决定,但您必须以某种方式将库的唯一实例存储在某处。

在所涉及的外部结构中实施 FFI 调用并不自然,因为某些调用可能涉及多个结构或根本 none。那么,你会把它们放在哪里?

您还提到了在 class 端实现方法的想法。毕竟,我们都同意每个库应该只有一个实例,不是吗?放弃这种可能性的一个原因是 class 副方法会提供不太灵活的实现。为什么?因为一件事是使用某种机制让 class 只有一个实例,另一件事是让它不可能拥有它。如果您的对象是一个实例(而不是 class),您仍然可以避免对单个实例的明确鼓励限制,并能够创建另一个实例。这会违反您自己的规则,但能够这样做总是更好。想要这样做的一个简单案例是测试。您可以创建第二个实例,它连接到另一个版本的库,并在无需修改 class 的情况下对其进行测试。不选择 class 端方法的另一个原因更微妙:classes 表示事物的概念,而不是事物的概念。因此,他们的自然协议不同于他们实例的协议。将两个界面分开将使您的设计更加清晰。

我会选择 ExternalLibrary 方法,因为它允许您自定义库名称,而不是在每个方法中对其进行硬编码。