我必须处理具有这种定义的外部库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 long
和unsigned 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 方言中采用的方法。
- 每个字段都有共享变量,将字段名称与其在结构中的偏移量相关联
- 默认情况下,所有这些偏移都适用于 32 位目标平台
- 使用这些共享变量(而不是文字数字)编写访问器方法
- 有一个
#offsets64
类端方法,该方法使用 64 位偏移量字典进行回答 - 启动时,如果系统运行 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