Быстрое соединение 2D-массива для типа UnsafePointer<UnsafeMutablePointer <Double>?>?
Привет, я пытаюсь обернуть C API, используя Swift 4
Swift импортировал функцию со следующей подписью.
public typealias indicator = @convention(c) (Int32, UnsafePointer<UnsafePointer<Double>?>?, UnsafePointer<Double>?, UnsafePointer<UnsafeMutablePointer<Double>?>?) -> Int32
в соответствии с документами C библиотеки, подпись выглядит следующим образом:
int indicator(int size,
double const *const *inputs,
double const *options,
double *const *outputs);
Стоит отметить, что int
возврат из функции в стиле c - тип ошибки функции, фактический возврат - в outputs
указатель
Итак, предполагая, что я создаю следующие типы Swift
let inputs: [[Double]] = []
let options: [Double] = []
var outputs: [[Double]] = []
с некоторыми соответствующими значениями, то я должен быть в состоянии сделать что-то вроде: (примечание info.pointee.indicator
это импортированная функция)
internal func calculateIndicator(options opts: [Double], input inputs: [[Double]], output outPuts: inout [[Double]]) -> [[Double]]? {
guard let sz = inputs.first?.count else {fatalError("Must supply a [[Double]] input param")}
let inputPointer = UnsafePointer<[Double]>(inputs)
let optionsPointer = UnsafePointer<Double>(opts)
var outputPointer = UnsafeMutablePointer<[Double]>(&outPuts)
let val = info.pointee.indicator(Int32(sz), inputPointer, optionsPointer, outputPointer)
// do something with the outputs and return the values
}
однако компилятор жалуется на следующую ошибку:
Cannot invoke 'indicator' with an argument list of type '(Int32, UnsafePointer<[Double]>, UnsafePointer<Double>, UnsafeMutablePointer<[Double]>)'
Этот вид имеет смысл, поскольку я передаю неправильные типы (я думаю).
Итак, вопросы управления памятью в сторону, как я буду идти о преобразовании [[Double]]
типы, например, к UnsafePointer<UnsafeMutablePointer<Double>>
указатель?
в соответствии с документами, приведенными здесь. Вызов функций с параметрами указателя Я должен быть в состоянии сделать это с неявным мостовым соединением, но это не так, возможно, мне следует просто создать типы указателей, а не пытаться конвертировать из Swift?
Заранее спасибо, я уверен, что упускаю что-то простое.
Сам API C выглядит следующим образом:
typedef int (*indicator_function)(int size,
double const *const *inputs,
double const *options,
double *const *outputs);
typedef struct indicator_info {
char *name;
char *full_name;
indicator_start_function start;
indicator_function indicator;
int type, inputs, options, outputs;
char *input_names[MAXINDPARAMS];
char *option_names[MAXINDPARAMS];
char *output_names[MAXINDPARAMS];
} indicator_info;
indicator
Функция доступна через структуру выше.
Данный экземпляр функции индикатора выглядит следующим образом
int add(int size,
TI_REAL const *const *inputs,
TI_REAL const *options,
TI_REAL *const *outputs);
2 ответа
Проблема здесь заключается в том, что C API требует эти параметры double *const *outputs
а также double const *const *inputs
или в кратчайшие сроки [[Double]]
типы.
это C
Сигнатура функции импортируется Swift в следующие типы соответственно.
UnsafePointer<UnsafeMutablePointer<Double>?>
UnsafePointer<UnsafePointer<Double>?>
Хотя легко из моста [T]
в UnsafePointer<T>
это не так легко идти к громоздкому UnsafePointer<UnsafePointer<T>>
а также UnsafePointer<UnsafeMutablePointer<T>>
, Нет также никакой документации, которую я могу найти, связанной с этими преобразованиями.
Я нашел отличный пост в блоге относительно указателей на UInt8
Массивы Ole Begemann, которые помогли мне пройти большую часть пути, в блоге " Передача массива строк из 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)
Волшебство - все о проходящих замыканиях между функциями и, таким образом, гарантируя, что мы не избежим мостовых указателей, решение Оле Бергманна отлично подходит для String
типы, но, надеюсь, это помогает кому-то еще, кто застрял с типом [[T]]
Предполагая, что, как вы заявляете в своем комментарии, у вас есть функция C, которую Swift набрал следующим образом:
public func indicator_abs(_ size: Int32,
_ inputs: UnsafePointer<UnsafePointer<Double>?>!,
_ options: UnsafePointer<Double>!,
_ outputs: UnsafePointer<UnsafeMutablePointer<Double>?>!) -> Int32
... тогда я думаю, что вы можете назвать это следующим образом:
let inputs = [1.0, 2.0]
let options = [1.0, 1.0]
var outputs = [0.0, 0.0]
let result:Int32 = withUnsafePointer(to: inputs) { inputsPtr in
withUnsafePointer(to: &outputs) { outputsPtr in
indicator_abs(2,inputsPtr,options,outputsPtr)
}
}