проблема здесь заключается в том, что API C требует эти параметры double *const *outputs
и double const *const *inputs
или в терминах Swift * типы 1003 *.
Эта сигнатура функции C
импортируется Swift вследующие типы соответственно.
UnsafePointer<UnsafeMutablePointer<Double>?>
UnsafePointer<UnsafePointer<Double>?>
В то время как легко преодолеть от [T]
до UnsafePointer<T>
, не так легко добраться до громоздких UnsafePointer<UnsafePointer<T>>
и UnsafePointer<UnsafeMutablePointer<T>>
.Нет также никакой документации, которую я могу найти, связанной с этими конверсиями.
Я нашел отличную запись в блоге относительно указателей на массивы UInt8
от Оле Бегеманна, которая дала мне большую часть пути, блог Передача массива строк из Swift в C .
. В этом он создает указатель UnsafeMutableBufferPointer
на тип [String]
, а затем перепривязывает память, как можно видеть ниже, и затем переходитЧтобы использовать смещения CChar
массивов, вы можете прочитать об этом в указанной выше статье
public func withArrayOfCStrings<R>(
_ args: [String],
_ body: ([UnsafeMutablePointer<CChar>?]) -> R) -> R {
let argsCounts = Array(args.map { $0.utf8.count + 1 })
let argsOffsets = [ 0 ] + scan(argsCounts, 0, +)
let argsBufferSize = argsOffsets.last!
var argsBuffer: [UInt8] = []
argsBuffer.reserveCapacity(argsBufferSize)
for arg in args {
argsBuffer.append(contentsOf: arg.utf8)
argsBuffer.append(0)
}
return argsBuffer.withUnsafeMutableBufferPointer { (argsBuffer) in
let ptr = UnsafeMutableRawPointer(argsBuffer.baseAddress!).bindMemory(
to: CChar.self, capacity: argsBuffer.count)
var cStrings: [UnsafeMutablePointer<CChar>?] = argsOffsets.map { ptr + $0 }
cStrings[cStrings.count - 1] = nil
return body(cStrings)
}
}
Поскольку экранирование указателей с мостами является неопределенным поведением согласно документам , этонеобходимо выполнить связывание и вызов внутри замыкания, как предлагает Оле Бергманн в своей статье.
Для этого мы создаем похожую функцию:
func indicatorWithArrays<R>(inputs ins:[[Double]],
options opts: [Double],
outputs out: [[Double]],
ti body: ([UnsafePointer<Double>?],
UnsafePointer<Double>?,
[UnsafeMutablePointer<Double>?]) -> R) -> R
, это универсально для типа R
его тип возврата, как и раньше.
Внутри функции мы соединяем входы и выходы с UnsafeBufferPointer
и затем вызываем map
в результирующих буферах для создания переменнойспособный типа [UnsafePointer<Double>]
, который затем может быть передан в тело замыкания.
return ins.withUnsafeBufferPointer { (inputsBuffer) in
var inPuts: [UnsafePointer<Double>?] = inputsBuffer.map { UnsafePointer($0) }
return out.withUnsafeBufferPointer { (outputsBuffer) in
var outPtrPtr: [UnsafeMutablePointer<Double>?]
= outputBuffer.map { UnsafeMutablePointer(mutating: $0) }
return body(inPuts, opts, outPtrPtr)
}
}
Передача параметра [UnsafePointer<Double>]
в замыкание body
неявно соединяет необходимые UnsafePointer<UnsafePointer<Double>>
и UnsafePointer<UnsafeMutablePointer<Double>>
требуется импортируемым C API.
Функция indicatorWithArrays
вызывается следующим образом и позволяет нам затем использовать мостовые указатели в импортированной функции C:
return indicatorWithArrays(inputs: input, options: opts, outputs: resArray) { (input, opts, outputs) in
let sz = inputs?.first?.count ?? 0
switch TIReturnType(rawValue: tulipInfo.info.pointee.indicator(Int32(sz), input, opts, outputs)) {
case .ti_okay?:
for (index, item) in outputs.enumerated() {
let buff = UnsafeBufferPointer(start: item, count: resArray[index].count)
resArray[index] = Array(buff)
}
return resArray
case nil:
return nil
}
}
, гдеВызов: tulipInfo.info.pointee.indicator(Int32(sz), input, opts, outputs)
Magic - это все прохождения замыканий между функциями и, таким образом, гарантирующие, что мы не избежим мостовых указателей, решение Оле Бергманна прекрасно для String
печатает, но, надеюсь, это поможет кому-то еще, кто застрял с типом [[T]]