如何在 Squeak FFI 中同时支持 32 位和 64 位目标



我必须处理具有这种定义的外部库struct foo {float *data; size_t len;};

首先我在 Squeak 中定义相应的结构

ExternalStructure subclass: #Foo 
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FFI-Tests'.

然后 Squeak FFI 可以处理不同的指针大小。
我知道我将不得不通过Foo defineFields重新生成字段访问。 但这是我可以在包加载后记中处理的事情,例如。

不幸的是,Squeak FFI中没有size_t支持。在我想要支持的平台上,我知道它将是uint32_t和uint64_t,这分别转换为 Squeak FFI 中的unsigned longunsigned long long(这些类型是从 Squeak 图像大小查看的固定大小)。

那我该怎么办?对Foo有两种不同的定义(但是我必须使用foo复制所有类型,并使用foo复制原型的接口)

Foo32 class>fields
^#(
(data 'float*')
(len 'ulong')
)

Foo64 class>fields
^#(
(data 'float*')
(len 'ulonglong')
)

或者是否有任何其他解决方案(例如使用Smalltalk wordSize=4 ifTrue: [] ifFalse: []手动定义字段/偏移量/字节大小)?

我不能告诉你如何在 Squeak 中做到这一点,但我可以与你分享我们在我使用的 Smalltalk 方言中采用的方法。

  1. 每个字段都有共享变量,将字段名称与其在结构中的偏移量相关联
  2. 默认情况下,所有这些偏移都适用于 32 位目标平台
  3. 使用这些共享变量(而不是文字数字)编写访问器方法
  4. 有一个#offsets64类端方法,该方法使用 64 位偏移量字典进行回答
  5. 启动时,如果系统运行 64 位,请使用#offsets64字典将共享关联的值替换为 64 位偏移量。这不需要在访问器上提供条件逻辑,也不需要重新编译方法。如果字段的相对位置随平台而变化,它也可以很好地工作。

请注意,仅当结构布局取决于位长度(并非总是如此)时,才需要步骤 4 和 5。

如果您的系统支持"本地池字典",则可以简化所有这些,这些字典是具有类变量范围的池字典。

从 mt.101 开始,您还可以对原子类型使用轻量级别名,这些别名通常在ExternalType class上作为消息实现,通常在"类型常量"协议中。见 source.squeak.org/FFI/FFI-Kernel-mt.101.diff

已经有从<stddef.h><stdint.h>已知的 typedef 的常见别名,例如size_t(或uintptr_t),您可以在外部结构的字段定义和 FFI 调用的参数定义中使用。

您的示例现在可以以跨平台的方式编写:

Foo class>>fields
^ #(
(data 'float*')
(len 'size_t')
)

现在我发现了别名(typedef),如如何在Squeak FFI中处理typedefs所报告的那样,我看到了一个解决方案:
定义一个Size_t别名

ExternalStructure subclass: #'Size_t'
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FFI-Tests'

然后定义平台相关字段:

Size_t class>>fields
^(Smalltalk wordSize = 4)
ifTrue: [#( nil 'ulong' )]
ifFalse: [#( nil 'ulonglong' )]

然后做Size_t defineFields,现在我有一个Size_t类型,我可以在函数原型和组合其他结构中使用。布局在 32 位和 64 位图像上会有所不同,但我不必更改客户端代码。

对于特定于平台的"无符号长"来说,这有点问题,因为Windows 64位上的LLP64是支持的平台之一:

ExternalStructure subclass: #'Unsigned_long'
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'FFI-Tests'
Unsigned_long class>>fields
^(Smalltalk wordSize = 4 or: [Smalltalk platformName = 'Win64'])
ifTrue: [#( nil 'ulong' )]
ifFalse: [#( nil 'ulonglong' )]

然后,我必须在映像启动时检查平台上的更改并重新编译所有 ExternalType。
此重新编译必须以正确的顺序执行,再一次,我在 Squeak FFI 中看不到任何支持,这将值得更多工作......

编辑我修改了 Squeak FFI,以便在另一个平台上恢复图像时引入自动重新编译支持,以及嵌套结构的正确初始化顺序。
见 http://source.squeak.org/FFI/FFI-Kernel-nice.49.diff

最新更新