"Downcasting" C Structs in Swift
Asked Answered
M

3

8

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"?

Mcgowen answered 29/12, 2014 at 13:48 Comment(0)
H
2

I suggest you look into the standard-library function unsafeBitCast.

Historic answered 29/12, 2014 at 13:52 Comment(0)
C
2

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.

Cantabile answered 29/12, 2014 at 14:16 Comment(1)
Update: Well it worked for a while. It's now broken in Swift 2.0 beta.Mcgowen
D
1

You are forgetting one thing. Even it Obj-C the casting always happens on pointers. You cannot cast memory to memory (well, sometimes you can, reinterpretting it, but it's not very safe).

let notification = np.memory

switch (notification.messageID) {            
    case MIDINotificationMessageID(kMIDIMsgObjectAdded):
       //cast the pointer!
       let addedNp = UnsafeMutablePointer<MIDIObjectAddRemoveNotification>(np)
       //now you can just access memory directly
       var m = addedNp.memory
}
Dissension answered 28/7, 2015 at 10:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.