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