Using Swift CFunctionPointer to pass a callback to CoreMIDI API
Asked Answered
A

2

16

It may be that this is actually not possible currently, which would be unfortunate. I'm trying to call the CoreMIDI API to set up a MIDI input. This is what I'm trying to do in Swift:

var midiClient = MIDIClientRef()
var inputPort = MIDIEndpointRef()
var status: OSStatus

func readProc(packetList: UnsafePointer<MIDIPacketList>, readProcRefCon: UnsafeMutablePointer<Void>, srcConnRefCon: UnsafeMutablePointer<Void>) -> Void {
}

status = MIDIClientCreate("MIDI client", nil, nil, &midiClient);

status = MIDIDestinationCreate(midiClient, "MIDI input", readProc, nil, &inputPort);

But I get this error: '(UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) -> Void' is not convertible to 'MIDIReadProc'

MIDIReadProc's typedef is the following:

typealias MIDIReadProc = CFunctionPointer<((UnsafePointer<MIDIPacketList>, UnsafeMutablePointer<Void>, UnsafeMutablePointer<Void>) -> Void)>

Is there a way to get a function pointer for my readProc method to pass to the MIDIDestinationCreate API?

Australopithecus answered 26/8, 2014 at 19:56 Comment(0)
S
25

In Swift 2.0 (as part of Xcode 7), C APIs that deal in function pointers use function types that are annotated @convention(c). You can pass any Swift function, method, or closure as a @convention(c) function type — but only if that closure conforms to C conventions... e.g. it can't capture state from its surrounding scope.

For details, see Type Attributes in The Swift Programming Language.


As for what's in Xcode 6: Swift 1.x doesn't have a way to convert a Swift function or closure to a C function pointer -- the sole use of the CFunctionPointer type is to pass function pointers imported from (Obj)C APIs to other (Obj)C APIs.

You can declare a function pointer in C code that you expose to Swift via your project's bridging header, then use Swift to pass that to CoreMIDI. But since you're going to be reaching across a bridge anyway, you might instead think about which parts of your project are best to keep in C and what the best interface is from those parts to your Swift code is.

Semipalatinsk answered 26/8, 2014 at 20:38 Comment(4)
Huh. Well, that's too bad. Thanks for your help.Australopithecus
A big reason it's not ready as a systems language, sadly. But what's another year?Precessional
@Semipalatinsk Did that change with Swift 1.2 that comes with XCode 6.3?Scheider
Doesn't look like it, in the code for CFunctionPointer: /// Though not directly useful in Swift, CFunctionPointer<T> can be used to safely pass a C function pointer, received from one C or Objective-C API, to another C or Objective-C API.Synonymous
C
19

Swift 1.x (Old Way)

There's a way to do that - Objective-C Runtime is the trick.

import CoreMIDI

let block : @objc_block
(UnsafePointer<MIDIPacketList>,
UnsafeMutablePointer<Void>,
UnsafeMutablePointer<Void>) -> Void =
{ (pktlist,readProcRefCon,srcConnRefCon) in

    //Your code goes here...
}

let imp : COpaquePointer =
    imp_implementationWithBlock(unsafeBitCast(block, AnyObject.self))

let callback : MIDIReadProc = unsafeBitCast(imp, MIDIReadProc.self)

Works with CoreFoundation callbacks. Should work for CoreMIDI too.


Swift 2.x (New Way)

In Swift 2 the process becomes "less hacky" (and slightly more readable).

import CoreMIDI

let callback : @convention(c) (pktlist : UnsafePointer<MIDIPacketList>,
                               readProcRefCon : UnsafeMutablePointer<Void>,
                               srcConnRefCon : UnsafeMutablePointer<Void>) -> Void =

{ (pktlist, readProcRefCon, srcConRefCon) in

}

let usableCallback = unsafeBitCast(callback, MIDIReadProc.self)
Calvillo answered 31/3, 2015 at 17:47 Comment(1)
Is that syntax documented in the Swift documentation anywhere ?Afterwards

© 2022 - 2024 — McMap. All rights reserved.