无法从捕获Swift包上下文的本地函数形成C函数指针

问题描述 投票:2回答:2

我正在尝试将C库用于应该在macOS和Linux上运行的机器人项目。我试图在作为库调用参数传递的C函数内调用Swift回调函数。

我尝试了herehere提出的解决方案,但它们不起作用。

正如在那些答案中所建议的那样,我传入传递给C函数的userData(或类似)对象,该函数可以调用Swift回调函数。

但是当我访问传递的userData对象时,我在Thread 2: EXC_BAD_ACCESS (code=1, address=0x20)函数的第二行得到了cHandler错误。我无法弄清楚原因。

这里的代码:

public func subscribe(newMessageHandler: @escaping () -> Void) -> Result<Subscription> {

    func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?) {
        guard let userData = userData else { return }
        let subscribeUserData = Unmanaged<SubscribeUserData>.fromOpaque(userData).takeUnretainedValue()
        subscribeUserData.handler()
    }

    let userData = SubscribeUserData(handler: newMessageHandler)
    var userDataPointer = UnsafeRawPointer(Unmanaged.passUnretained(userData).toOpaque())

    self.subscribeUserData = userData
    self.subscribeUserDataPointer = userDataPointer

    if let subscription = lcm_subscribe(context, "ExampleMessage", cHandler, &userDataPointer) {
        return .success(subscription)
    } else {
        return .failure(nil)
    }
}

这是SubscribeUserData的定义,我在C函数中传递的对象:

typealias NewMessageHandler = () -> Void

/// User data object passed in the subscribe C handler function. Needed to pass in a Swift handler function.
class SubscribeUserData {
    let handler: NewMessageHandler
    init(handler: @escaping NewMessageHandler) {
        self.handler = handler
    }
}
swift
2个回答
2
投票

感谢Andy给了我不同的建议让我解决了这个问题。

一个问题是我将UnsafeMutableRawPointer传递给前缀为&运算符的cHandler函数。

第二个问题是我在cHandler函数内传递的对象被取消分配。所以保持对它的引用是至关​​重要的。

这里的工作代码:

public func subscribe(newMessageHandler: @escaping NewMessageHandler) -> Result<Subscription> {

    func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?) {
        guard let userData = userData else { return }
        let subscribeUserData = Unmanaged<SubscribeUserData>.fromOpaque(userData).takeUnretainedValue()
        subscribeUserData.handler()
    }

    self.subscribeUserData = SubscribeUserData(handler: newMessageHandler)
    let subscribeUserDataPointer = UnsafeMutableRawPointer(Unmanaged.passUnretained(subscribeUserData).toOpaque())

    if let subscription = lcm_subscribe(context, "ExampleMessage", cHandler, subscribeUserDataPointer) {
        return .success(subscription)
    } else {
        return .failure(nil)
    }
}

感谢大家的帮助!


0
投票

更新

仔细观察one of your examples,你似乎在做同样的事情,这让我想知道你的处理程序签名。

func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>?, channel: UnsafePointer<Int8>?, userData: UnsafeMutableRawPointer?)

我发现了一个lcm_subscribe

这是指lscm_msg_handler_t

typedef void(* lcm_msg_handler_t) (const lcm_recv_buf_t *rbuf, const char *channel, void *user_data)

仔细观察这个签名让我注意到两个问题:

  1. 您在指针上使用了addressOf运算符&。这是不正确的。要么对变量使用该运算符,要么按照您的方式创建指针(所以只需删除&)。
  2. 您对cHandler的所有参数都被声明为选项。这也是错误的 - 选项是Swift的概念。您的回调签名是发布C可以理解的函数。

func cHandler(buffer: UnsafePointer<lcm_recv_buf_t>, channel: UnsafePointer<Int8>, userData: UnsafeMutableRawPointer)


糟糕的第一个想法

我非常警惕这里的结构和生命。

代替

let userData = SubscribeUserData(handler: newMessageHandler)
var userDataPointer = UnsafeRawPointer(Unmanaged.passUnretained(userData).toOpaque())

self.subscribeUserData = userData
self.subscribeUserDataPointer = userDataPointer

获取指向您的成员的指针并立即使用返回的结构。

self.subscribeUserData = SubscribeUserData(handler: newMessageHandler)
self.subscribeUserDataPointer = UnsafeRawPointer(Unmanaged.passUnretained(self.subscribeUserData).toOpaque())

特别是,我认为危险是在passUnretained中使用局部变量。

如果你认为内部模型是一个指向包含引用的位置的指针,那么它就更有意义了 - 你的原始文件正在获取指向堆栈上局部变量的指针。

© www.soinside.com 2019 - 2024. All rights reserved.