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

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

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

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


person distorteddisco    schedule 26.08.2014    source источник


Ответы (2)


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

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


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

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

person rickster    schedule 26.08.2014
comment
Хм. Что ж, это очень плохо. Спасибо за вашу помощь. - person distorteddisco; 27.08.2014
comment
К сожалению, основная причина, по которой он не готов как системный язык. Но какой еще год? - person uchuugaka; 20.12.2014
comment
@rickster Изменилось ли это с Swift 1.2, который поставляется с XCode 6.3? - person George Yacoub; 09.03.2015
comment
Не похоже, в коде для 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. - person Adam; 16.03.2015

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)
person Matteo Pacini    schedule 31.03.2015
comment
Этот синтаксис задокументирован где-нибудь в документации Swift? - person Alnitak; 12.04.2015