Быстрое соединение 2D Array для ввода UnsafePointer? - PullRequest
0 голосов
/ 20 декабря 2018

Привет, я пытаюсь обернуть 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>>?

в соответствии с документами здесь Вызов функций с параметром указателяs Я должен быть в состоянии сделать это с неявным мостовым соединением, но, похоже, нет, возможно, мне следует просто создать типы указателей, а не пытаться конвертировать из Swift?

Заранее спасибо, я уверен, что яЯ упускаю что-то простое.

Сам C API выглядит следующим образом:

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 ]

0 голосов
/ 04 января 2019

проблема здесь заключается в том, что 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]]

0 голосов
/ 20 декабря 2018

Предполагая, что, как вы заявляете в своем комментарии, у вас есть функция C, которую Свифт напечатал следующим образом:

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)
        }
    }
...