Search code examples
cswiftcallbacklanguage-interoperability

Call C function from Swift knowing only memory address


I have a very simple function in C and its pointer gets passed into Swift.

void callback() {
    NSLog(@"such pointer…");
}

How can it be invoke in Swift using ONLY the pointer? This is NOT about simply invoking it – it works fine when invoked directly. Tried the following and different variations with signature casting, but it always crashes:

let pointer: UnsafePointer<@convention(c)() -> ()> = UnsafePointer(bitPattern: …)
let callback: @convention(c)() -> () = pointer.memory
Swift.print(callback) // (Function)
callback() // Handling crash with signal 11...

Tried defining Callback type with typedef void(*Callback)(); – same thing:

let pointer: UnsafePointer<Callback> = UnsafePointer(bitPattern: …)
let callback: Callback = pointer.memory
Swift.print(callback) // (Function)
callback() // Handling crash with signal 11...

To check that it actually works and points to the right address I have another Objective-C function, which works as expected when invoked from Swift with the callback pointer.

void invokeCallback(uintptr_t callback) {
    ((void (*)()) callback)(); // such pointer…
}

Solution

  • The same approach as in Swift: How to call a C function loaded from a dylib should work here.

    Example for Swift 3 (assuming that funcPtr is a UnsafeRawPointer containing the function's address):

    // Define function type:
    typealias callbackFunc = @convention(c) () -> Void
    
    // Convert pointer to function type:
    let callback = unsafeBitCast(funcPtr, to: callbackFunc.self)
    
    // Call function:
    callback()
    

    Of course the type alias must match the actual function signature, and the function needs to be a "pure" C function (not an Objective-C method).