Подтвердить что ты не робот

Использование Swift CFunctionPointer для передачи обратного вызова API CoreMIDI

Возможно, в действительности это невозможно, что было бы неудачно. Я пытаюсь вызвать CoreMIDI API для настройки MIDI-входа. Это то, что я пытаюсь сделать в 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);

Но я получаю эту ошибку: '(UnsafePointer, UnsafeMutablePointer, UnsafeMutablePointer) → Void' не конвертируется в 'MIDIReadProc'

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

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

Есть ли способ получить указатель на функцию моего метода readProc для API MIDIDestinationCreate?

4b9b3361

Ответ 1

В Swift 2.0 (в настоящее время в бета-версии, как часть бета-версии Xcode 7) API-интерфейсы C, которые работают с указателями функций, используют типы функций, аннотированные @convention(c). Вы можете передать любую функцию, метод или закрытие Swift как тип функции @convention(c), но только если это закрытие соответствует соглашениям C... например. он не может захватить состояние из своего окружения.

Подробнее см. Атрибуты типа на языке Swift.


Что касается того, что в настоящее время выходит из бета-версии... Swift 1.x не имеет возможности преобразовать функцию Swift или закрытие в указатель функции C - единственным использованием типа CFunctionPointer является передача функции указатели, импортированные из API (Obj) C в другие API (Obj) C.

Вы можете объявить указатель функции в коде C, который вы выставляете в Swift через свой заголовок проекта, а затем использовать Swift для передачи этого в CoreMIDI. Но поскольку вы все равно собираетесь пересекать мост, вы можете подумать о том, какие части вашего проекта лучше всего хранить на C, и какой лучший интерфейс от этих частей к вашему SWIFT-коду.

Ответ 2

Swift 1.x(Старый путь)

Есть способ сделать это - Objective-C Runtime - это трюк.

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)

Работает с обратными вызовами CoreFoundation. Должен работать и для CoreMIDI.


Swift 2.x(новый способ)

В Swift 2 процесс становится "менее взломанным" (и немного более читаемым).

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)