swift 如何将结构数组传递给C API?

oxiaedzo  于 2022-11-21  发布在  Swift
关注(0)|答案(1)|浏览(118)

我有一个C API,看起来像这样:

typedef struct Datapoints {
  double *ptr;
  uintptr_t len;
} Datapoints;

double my_function(const struct Datapoints *baseline_data,
                     uint32_t baseline_data_len,
                     const struct Datapoints *new_data);

第一个参数是一个指向Datapoints结构体的指针数组,第三个参数是一个Datapoints结构体。
这是我的想法,但是它不能工作有几个原因,主要的一个是我试图存储一个不安全指针的列表,以便稍后将它们传递给my_function。我通过重命名变量和类型使代码变得有点混乱,所以如果它不能编译,请原谅我。代码基本上如下所示:

var baseline_datapoints = [Datapoints]()
var current_datapoints:Datapoints = Datapoints()

// This loop was intended to build up an array `baseline_Datapoints` containing pointers to struct `Datapoints`
for reading in baseline {
    let datapoints:[Double] = reading.datapoints

    // Won't work: The pointer passed as an argument to `body` is valid only during the
    // execution of `withUnsafeMutableBufferPointer(_:)`. Do not store or return
    // the pointer for later use.
    // We _are_ attempting to store it for later use. 🤷🏻

    datapoints.withUnsafeMutableBufferPointer{ ptr in
        baseline_datapoints.append(Datapoints(ptr: ptr.baseAddress, len: UInt(datapoints.count)) )
    }
}

self.newData.withUnsafeMutableBufferPointer {ptr in
    current_datapoints = Datapoints(ptr: ptr.baseAddress, len: UInt(self.newData.count))
}

let x = baseline_datapoints.withUnsafeBufferPointer{
    (baselineptr: UnsafeBufferPointer<Datapoints>) -> Double in
    return withUnsafePointer(to: current_datapoints) {
        (current_ptr: UnsafePointer<Datapoints>) -> Double in

        return my_function(baseline_ptr.baseAddress, UInt32(baseline_datapoints.count ), current_ptr)
    }
}

我 * 认为 * 我应该做的是扩展withUnsafeXxxPointer闭包的作用域,以便数据保持足够长的有效时间。但是,我不确定在尝试构建baseline_data列表时如何做。
而且,对每个不安全指针使用一个闭包似乎不方便。如果我这样做,会导致代码嵌套很深。有没有其他方法可以做到这一点?
欢迎提出任何建议。
编辑:
除了第三个参数是按值传递的之外,这个公认的答案工作得很好。然而,无论如何,这对C API来说是一个更好的设计,所以我把我的API改为:

double my_function(const struct Datapoints *baseline_data,
                     uint32_t baseline_data_len,
                     const struct Datapoints new_data);

baseline_datanew_data均在参数中,以备记录。

6bc51xsx

6bc51xsx1#

不幸的是,在Swift中,在传递给datapoints.withUnsafeMutableBufferPointer的闭包之外存储和重用ptr.baseAddress的值是不安全的。
...指针参数只在方法执行期间有效。
这意味着,在for循环 * 中无法执行您尝试执行的操作,而让baseAddressself.newData.withUnsafeMutableBufferPointer { }中逃逸的方法也是如此。
最干净、最安全的解决方案是需要实际 * 复制 * 基础Double。看起来更像这样:

var baseline_bufs = [UnsafeMutableBufferPointer<Double>]()
defer {
  // don't forget to deallocate memory that was allocated!
  for d in baseline_bufs {
    d.deallocate()
  }
}

// Copy data from baseline readings into UnsafeMutableBufferPointers
for reading in baseline {
  d = UnsafeMutableBufferPointer<Double>.allocate(capacity: reading.datapoints.count)
  d.initialize(from: reading.datapoints)
  baseline_bufs.append(d)
}

let x = self.newData.withUnsafeMutableBufferPointer { newDataBuf in
  let new_ptr = Datapoints(ptr: newDataBuf.baseAddress, len: UInt(newDataBuf.count))

  // convert each `UnsafeMutableBufferPointer` to a `Datapoints`
  let baseline_datapoints = baseline_bufs.map { Datapoints(ptr: $0.baseAddress, len: UInt($0.count)) }
  return baseline_datapoints.withUnsafeBufferPointer {
    return my_function($0.baseAddress, UInt32($0.count), new_ptr)
  }
}
  • 也许有 * 一些 * 方法可以使用递归嵌套所有withUnsafeX调用,但是您会遇到堆栈大小限制,这取决于数组中的项数。

相关问题