如何创建一个UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>?

12

我正在使用Swift处理一个C API,其中我需要调用其中的一种方法,但是我需要提供一个

UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>

更多信息:

Swift接口:

public func presage_predict(prsg: presage_t, _ result: UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>) -> presage_error_code_t

原始代码:

presage_error_code_t presage_predict(presage_t prsg, char*** result);

C函数是如何声明的,它又是做什么的呢? - Martin R
Swift接口:public func presage_predict(prsg: presage_t, _ result: UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>) -> presage_error_code_tC语言: presage_error_code_t presage_predict (presage_t prsg, char*** result); - Nykholas
@Nykholas,看起来你尝试使用Presage库进行预测。 我正在尝试在Xcode上构建它,但是失败了。 你能教我你是如何做的吗? - gstream
2个回答

18

通常,如果一个函数接受 UnsafePointer<T> 参数,那么你可以使用 & 将类型为 T 的变量作为 "inout" 参数传递。在你的情况下,T

UnsafeMutablePointer<UnsafeMutablePointer<Int8>>

这是char **的Swift映射。因此,您可以按照以下方式调用C函数:
var prediction : UnsafeMutablePointer<UnsafeMutablePointer<Int8>> = nil
if presage_predict(prsg, &prediction) == PRESAGE_OK { ... }

根据Presage库的文档和示例代码,我理解这将分配一个字符串数组并将该数组的地址分配给指向prediction变量的指针。为避免内存泄漏,这些字符串最终必须被释放。

presage_free_string_array(prediction)

为了证明这确实有效,我已经将presage_c_demo.c的演示代码的第一部分翻译成了Swift:
// Duplicate the C strings to avoid premature deallocation:
let past = strdup("did you not sa")
let future = strdup("")

func get_past_stream(arg: UnsafeMutablePointer<Void>) -> UnsafePointer<Int8> {
    return UnsafePointer(past)
}

func get_future_stream(arg: UnsafeMutablePointer<Void>) -> UnsafePointer<Int8> {
    return UnsafePointer(future)
}

var prsg = presage_t()
presage_new(get_past_stream, nil, get_future_stream, nil, &prsg)

var prediction : UnsafeMutablePointer<UnsafeMutablePointer<Int8>> = nil
if presage_predict(prsg, &prediction) == PRESAGE_OK {

    for var i = 0; prediction[i] != nil; i++ {
        // Convert C string to Swift `String`:
        let pred = String.fromCString(prediction[i])!
        print ("prediction[\(i)]: \(pred)")
    }

    presage_free_string_array(prediction)
}

free(past)
free(future)

这实际上是有效的并产生了输出

预测[0]: 说
预测[1]: 说过
预测[2]: 野蛮人
预测[3]: 看到
预测[4]: 坐着
预测[5]: 相同的

我认为您在预测声明中漏掉了一个UnsafeMutablePointer,同时我在字符串转换中遇到了以下错误:无法将类型为“UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>”的值转换为预期的参数类型“UnsafePointer<CChar>”,也就是说UnsafePointer<Int8>,此外,prediction不是一个数组。 - Nykholas
@Nykholas:我相当确定我没有错过指针。我已经使用presage头文件编译了该代码,并且没有收到任何警告或错误。 - Martin R
是的,它编译通过了,但我认为它是错误的,因为prediction的签名是错误的。根据头文件,你说它是char **而不是char ***,而且prediction似乎根本不是一个数组。我复制粘贴了你的代码,它从未进入for循环:(lldb)fr v prediction [0] 错误:“(Swift.UnsafeMutablePointer <Swift.UnsafeMutablePointer <Swift.Int8>>)prediction”不是数组类型 - Nykholas
@Nykholas:非常奇怪,对我来说编译和运行都没问题,产生了所示的结果,并且没有错误或警告。 - Martin R
奇怪的是,它并没有完全按照您输入的方式工作。我收到了一个错误:C函数指针只能由对函数或字面闭包的引用形成,但我将字符串作为参数传递,然后从闭包中获取它们,这样就可以工作了! - Nykholas
显示剩余4条评论

5

可能有更好的方法,但是这个方法可以在playground中运行,并且定义了一个类型为您所需的值r

func ptrFromAddress<T>(p:UnsafeMutablePointer<T>) -> UnsafeMutablePointer<T>
{
   return p
}

var myInt:Int8 = 0
var p = ptrFromAddress(&myInt)
var q = ptrFromAddress(&p)
var r = ptrFromAddress(&q)

定义ptrFromAddress有什么意义,它似乎什么也没做?我认为,在讨论可变指针的部分中,Swift互操作性书籍展示了通过将某些表达式作为参数传递(如&x)的多种初始化方式,但似乎没有对应的方式可以简单地调用UnsafeMutablePointer的初始化器。因此,让我们定义一个无操作的函数,以便使用那些基于参数传递的特殊初始化方法。

更新:

虽然我认为上面的方法是正确的,但在另一个论坛中,@alisoftware指出这似乎是一种更安全和更符合惯例的做法:

var myInt: Int8 = 0
withUnsafeMutablePointer(&myInt) { (var p) in
  withUnsafeMutablePointer(&p) { (var pp) in
    withUnsafeMutablePointer(&pp) { (var ppp) in
      // Do stuff with ppp which is a UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<Int8>>>
    }
  }
}

使用Swift标准库提供的函数withUnsafeMutablePointer比定义自己的帮助函数更符合惯用法。它更安全,因为您保证了UnsafeMutablePointer仅在调用闭包期间存在(只要闭包本身不存储指针)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接