Usando o Swift CFunctionPointer para passar um retorno de chamada para a API CoreMIDI

Pode ser que isso realmente não seja possível atualmente, o que seria lamentável. Estou tentando chamar a API CoreMIDI para configurar uma input MIDI. Isso é o que estou tentando fazer no Swift:

var midiClient = MIDIClientRef() var inputPort = MIDIEndpointRef() var status: OSStatus func readProc(packetList: UnsafePointer, readProcRefCon: UnsafeMutablePointer, srcConnRefCon: UnsafeMutablePointer) -> Void { } status = MIDIClientCreate("MIDI client", nil, nil, &midiClient); status = MIDIDestinationCreate(midiClient, "MIDI input", readProc, nil, &inputPort); 

Mas eu recebo este erro: ‘(UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) -> Void’ não é conversível para ‘MIDIReadProc’

O typedef do MIDIReadProc é o seguinte:

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

Existe uma maneira de obter um ponteiro de function para o meu método readProc para passar para a API MIDIDestinationCreate?

No Swift 2.0 (como parte do Xcode 7), as APIs de C que lidam com pointers de function usam tipos de function que são anotados como @convention(c) . Você pode passar qualquer function, método ou encerramento do Swift como um tipo de function @convention(c) – mas somente se esse fechamento estiver em conformidade com as convenções C … por exemplo, ele não pode capturar o estado do escopo circundante.

Para detalhes, consulte Atributos de tipo na linguagem de programação Swift .


Quanto ao que está no Xcode 6: O Swift 1.x não possui uma maneira de converter uma function Swift ou um fechamento para um ponteiro de function C – o uso exclusivo do tipo CFunctionPointer é passar pointers de function importados de APIs (Obj) C para outras APIs (Obj) C.

Você pode declarar um ponteiro de function no código C que você expõe ao Swift através do header de bridging do seu projeto, então use o Swift para passá-lo para o CoreMIDI. Mas como você vai alcançar uma ponte de qualquer maneira, você pode pensar em quais partes do seu projeto são melhores para manter em C e qual é a melhor interface dessas partes para o seu código Swift.

Swift 1.x (Old Way)

Existe uma maneira de fazer isso – o Runtime do Objective-C é o truque.

 import CoreMIDI let block : @objc_block (UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) -> 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) 

Funciona com retornos de chamada do CoreFoundation. Deve funcionar para o CoreMIDI também.


Swift 2.x (novo caminho)

No Swift 2, o processo torna-se “menos hacky” (e um pouco mais legível).

 import CoreMIDI let callback : @convention(c) (pktlist : UnsafePointer, readProcRefCon : UnsafeMutablePointer, srcConnRefCon : UnsafeMutablePointer) -> Void = { (pktlist, readProcRefCon, srcConRefCon) in } let usableCallback = unsafeBitCast(callback, MIDIReadProc.self)