3

Hi I am trying to wrap a C api using Swift 4

Swift has imported a function with the following signature.

public typealias indicator = @convention(c) (Int32, UnsafePointer<UnsafePointer<Double>?>?, UnsafePointer<Double>?, UnsafePointer<UnsafeMutablePointer<Double>?>?) -> Int32

according to the C libraries docs then the signature is as follows:

int indicator(int size, double const *const *inputs, double const *options, double *const *outputs);

It’s worth noting that the int return from the function is in c style the error type of the function, the actual return is in the outputs pointer

So then assuming I create the following Swift types

let inputs: [[Double]] = [] let options: [Double] = [] var outputs: [[Double]] = []

with some appropriate values then I should be able to do something like: ( note info.pointee.indicator is the imported function )

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
}

however the compiler complains with the following error:

Cannot invoke 'indicator' with an argument list of type '(Int32, UnsafePointer<[Double]>, UnsafePointer<Double>, UnsafeMutablePointer<[Double]>)'

This sort of makes sense as I am passing the incorrect types ( I think ).

So then memory management issues aside how would I go about converting the [[Double]] types to for example the UnsafePointer<UnsafeMutablePointer<Double>> pointer?

according to the docs here Calling Functions With Pointer Parameters I should be able to do this with implicit bridging but it seems not, perhaps I should just create the pointer types rather than try and convert from Swift?

Thanks in advance, I'm sure I'm missing something simple.

The C API itself is as follows:

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;

The indicator function is accessed through the struct above.

A given instance of an indicator function is as follows

int add(int size,
  TI_REAL const *const *inputs,
  TI_REAL const *options,
  TI_REAL *const *outputs);
lbdl
  • 353
  • 1
  • 4
  • 8
  • “Swift has imported a function with the following signature” No. According to that, `indicator` is not a function that you call. It is a type (signature) of a function that you pass. Show the C API where we actually have to pass it. – matt Dec 20 '18 at 02:15
  • well it is a type alias to a whole bunch of functions that all have the following signature `public func indicator_abs(_ size: Int32, _ inputs: UnsafePointer?>!, _ options: UnsafePointer!, _ outputs: UnsafePointer?>!) -> Int32`. I have edited the question. – lbdl Dec 20 '18 at 02:26

2 Answers2

2

the problem here lies in the fact that the C API requires these parameters double *const *outputs and double const *const *inputs or in Swift terms [[Double]] types.

This C function signature is imported by Swift into the following types respectively.

UnsafePointer<UnsafeMutablePointer<Double>?> 
UnsafePointer<UnsafePointer<Double>?>

Whilst easy to bridge from [T] to UnsafePointer<T> it's not so easy to go to the bulky UnsafePointer<UnsafePointer<T>> and UnsafePointer<UnsafeMutablePointer<T>>. Nor is there any documentation that I can find related to these conversions.

I did find an excellent blog post regarding pointers to UInt8 arrays by Ole Begemann which got me most of the way there, the blog is Passing an Array of Strings from Swift to C.

In this he creates a UnsafeMutableBufferPointer pointer to a [String] type and then rebinds the memory, as can be seen below and then goes on to use the offsets of the CChar arrays, you can read about this in the above referenced article

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

As escaping bridged pointers is undefined behaviour according to the docs it is necessary to do the bridging and calling inside a closure as Ole Bergmann suggests in his article.

To accomplish this we create a similar function:

func indicatorWithArrays<R>(inputs ins:[[Double]],
                            options opts: [Double],
                            outputs out: [[Double]],
                            ti body: ([UnsafePointer<Double>?], 
                                      UnsafePointer<Double>?, 
                                      [UnsafeMutablePointer<Double>?]) -> R) -> R

this is generic over type R its Return type as before.

Inside the function we bridge the inputs and outputs into UnsafeBufferPointer's and then call map on the resultant buffers to create a variable of type [UnsafePointer<Double>] which can then be passed to the closure body.

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

The passing of the [UnsafePointer<Double>] param to the body closure implicitly bridges to the required UnsafePointer<UnsafePointer<Double>> and the UnsafePointer<UnsafeMutablePointer<Double>> required by the imported C API.

The indicatorWithArrays function is called as follows and allows us to then use the bridged pointers in the imported C function:

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

where the call is: tulipInfo.info.pointee.indicator(Int32(sz), input, opts, outputs)

The Magic is all about the passing closures between the functions and thereby ensuring that we don't escape the bridged pointers, Ole Bergmann's solution is fine for String types but hopefully this helps someone else who is stuck with type [[T]]

lbdl
  • 353
  • 1
  • 4
  • 8
0

Assuming that you have, as you state in your comment, a C function that Swift has typed like this:

public func indicator_abs(_ size: Int32, 
    _ inputs: UnsafePointer<UnsafePointer<Double>?>!, 
    _ options: UnsafePointer<Double>!, 
    _ outputs: UnsafePointer<UnsafeMutablePointer<Double>?>!) -> Int32

...then I think you can call it as follows:

    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)
        }
    }
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    thanks but there are a couple of things, the parameters `inputs` and `outputs` are of type `[[Double]]` and also in C style `result` is the error type of the function the actual output needed in in `outputs`. So I don’t think this would work as I dont think we can escape the `outputsPtr` – lbdl Dec 20 '18 at 09:11