开发者问题收集

Swift:无法将函数指针转换为(void*)以供 C 风格第三方库使用

2017-05-09
1167

如何将 Swift 中的函数指针转换为 (void*) 以用于第三方 C 样式库?

我正在编写一个采集卡,需要使用第三方库提供的 C 样式函数设置中断回调。必须调用该特定函数来设置回调,该函数为回调接受 (void*) 参数。在 C 语言中,这是可行的:

// somefile.c
int PHX_StreamRead( tHandle, etAcq, void*);

PHX_StreamRead(handle, PHX_START, (void*)&phxvptrs_callback);

static void phxvptrs_callback(tHandle h, ui32 mask, void *p) {
     //... stuff
}

但在 Swift 中,我无法使用函数指针 phxvptrs_callback 调用 PHX_StreamRead(),因为出现编译错误(错误的指针类型):

// file.swift

PHX_StreamRead(handle, PHX_START, &phxvptrs_callback) // Does not compile
// Error: "Cannot pass immutable value as inout argument: phxvptrs_callback is a function"

但是,(至少对我来说令人惊讶),我将该指针转换为任何看起来像 UnsafeRawPointer 的东西的所有尝试都失败了:

// attempts.swift

typealias callbackFctType = ((tHandle, ui32, UnsafeMutableRawPointer) -> Void)

let p1 = UnsafeRawPointer(&phxvptrs_callback) // does not compile
// Error: "Cannot pass immutable value as inout argument: phxvptrs_callback is a function"

let p2 = UnsafePointer<callbackFctType>(&phxvptrs_callback) // Similar error

我想出了一个解决方法(如下)但 我想了解为什么这些转换拒绝编译 。我认为几乎任何东西都可以转换为 UnsafeRawPointer。

我的解决方法是创建一个带有回调定义和包装函数 MyPHX_StreamRead_START_WithCallBack() 的小型 C 文件,该文件仅使用正确的可接受 C 语法调用 PHX_StreamRead():

// file.c
static void phxvptrs_callback(tHandle h, ui32 mask, void *p) {
     //... stuff
}

int MyPHX_StreamRead_START_WithCallBack(tHandle handle) {
    return PHX_StreamRead(handle, PHX_START, &phxvptrs_callback);
}

当然,从 Swift 调用该包装器不是问题,并且解决了我的问题,但我发现这个解决方案“不迅速”。

1个回答

据我所知,在 Swift 中转换函数指针类型的唯一方法是使用 unsafeBitCast

尝试类似这样的操作:

PHX_StreamRead(handle, PHX_START, unsafeBitCast(phxvptrs_callback, to: UnsafeMutableRawPointer.self))

编辑

如果您收到“致命错误:无法在不同大小的类型之间进行 unsafeBitCast”,Swift 可能未将 phxvptrs_callback 视为 C 函数( @convention(c) 闭包)。在这种情况下,需要更多代码:

typealias callbackFctType = @convention(c) (tHandle, ui32, UnsafeMutableRawPointer?)->Void

let callbackWrapper: callbackFctType = phxvptrs_callback

PHX_StreamRead(handle, PHX_START, unsafeBitCast(callbackWrapper, to: UnsafeMutableRawPointer.self))

或者这将编译并按预期工作:

typealias callbackFctType = @convention(c) (tHandle, ui32, UnsafeMutableRawPointer?)->Void

PHX_StreamRead(handle, PHX_START, unsafeBitCast(phxvptrs_callback as callbackFctType, to: UnsafeMutableRawPointer.self))

编辑 2

要在 Swift 中编写回调,您可以编写类似这样的代码:

typealias callbackFctType = @convention(c) (tHandle, ui32, UnsafeMutableRawPointer?)->Void

let myCallback: callbackFctType = {handle, mask, p in
    //This closure cannot capture the context of its surrounding scope.
    //(Which means this closure cannot use `self` even when in a class.)
    //...
}

PHX_StreamRead(handle, PHX_START, unsafeBitCast(myCallback, to: UnsafeMutableRawPointer.self))
OOPer
2017-05-09