Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Downcasting" C Structs in Swift

Tags:

ios

swift

Core MIDI is a C API which provides capabilities not found elsewhere.

When the user's MIDI setup changes (e.g. you plugged in a device), there is a notification.

This is the type of the function that is called.

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

The first parameter is a MIDINotification struct which looks like this:

struct MIDINotification {
    var messageID: MIDINotificationMessageID
    var messageSize: UInt32
}

You might implement the callback like this:

func MyMIDINotifyProc (np:UnsafePointer<MIDINotification>, refCon:UnsafeMutablePointer<Void>) {        
    var notification = np.memory       
    switch (notification.messageID) {

    case MIDINotificationMessageID(kMIDIMsgObjectAdded):
        // In Objective-C you would just do a cast here
        // This is the problem line
        var m = np.memory as MIDIObjectAddRemoveNotification

You would look at the messageID member to see what kind of notification you just received. There are several (I'm showing just one). For each kind of notification, you will get a different struct passed in. This is the struct you get when a device has been added or removed:

struct MIDIObjectAddRemoveNotification { 
    var messageID: MIDINotificationMessageID
    var messageSize: UInt32
    var parent: MIDIObjectRef
    var parentType: MIDIObjectType
    var child: MIDIObjectRef
    var childType: MIDIObjectType
}

As you see, this struct has additional info. The "child" might be an endpoint to a device for example, so you need these fields.

The problem is casting from the MIDINotification struct (required by the callback signature) to MIDIObjectAddRemoveNotification. The line I've shown using "as" does not work.

Do you have any suggestions for this sort of "downcasting"?

like image 717
Gene De Lisa Avatar asked Dec 29 '14 13:12

Gene De Lisa


1 Answers

As Vatsal Manot suggested, since MIDINotification and MIDIObjectAddRemoveNotification are not related by any inheritance or contract, Swift cannot perform any safe casting between those structures.

You'll need to cast it explicitly using unsafeBitCast function:

case MIDINotificationMessageID(kMIDIMsgObjectAdded):
    let m = unsafeBitCast(np.memory, MIDIObjectAddRemoveNotification.self)

Note that this function can always be used in Swift to perform casts, but it is extremely unsafe and you should use it only as the last possible solution.

like image 190
akashivskyy Avatar answered Sep 26 '22 03:09

akashivskyy