【问题标题】:How one supports both 32 and 64 bits target in Squeak FFI如何在 Squeak FFI 中同时支持 32 位和 64 位目标
【发布时间】:2018-04-11 21:10:35
【问题描述】:

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

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

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

那么 Squeak FFI 可以处理不同的指针大小。
我知道我必须通过Foo defineFields 重新生成字段访问权限。 但这是我可以在包加载 postscript 中处理的事情。

不幸的是,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: [])?

【问题讨论】:

    标签: smalltalk ffi squeak


    【解决方案1】:

    从 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')
          )
    

    【讨论】:

      【解决方案2】:

      我无法告诉您如何在 Squeak 中执行此操作,但我可以与您分享我们在使用的 Smalltalk 方言中采用的方法。

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

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

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

      【讨论】:

      • 这很有用,但必须自动生成偏移量,这需要对 FFI 本身进行更深入的更改。就目前而言,我使用当前 FFI 提出了一个功能较弱但可行的解决方案。
      【解决方案3】:

      现在我发现了 How one deals with typedefs in Squeak FFI 报告的别名 (typedef),我看到了一个解决方案:
      定义一个 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

      【讨论】:

      • 拥有一个 ABI 对象也会有所帮助。您需要询问字长的地方非常多,因此向当前 ABI 对象(SysVx86ABI、SysVX64ABI、Win64ABI 等)询问指针类型之类的东西要优雅得多
      • 自mt.75起,有FFIPlatformDescription,实现平台变化时启动时自动重新编译。见source.squeak.org/FFI/FFI-Kernel-mt.75.diff
      猜你喜欢
      • 2018-03-17
      • 2012-03-20
      • 2015-08-09
      • 1970-01-01
      • 2014-05-27
      • 2015-03-19
      • 2018-07-10
      • 1970-01-01
      相关资源
      最近更新 更多