drummingdemon
drummingdemon

Reputation: 45

Using a C API function in Swift 3 that has a callback function pointer as an argument

I'm building a wrapper/interface for a C library with Swift 3. One of the functions that I need to call require a function pointer for callback as an argument.

In detail: after said function has successfully done the file operation it has to, it then calls the function the given argument pointer refers to - essentially letting me do other operations with said data

The function looks like this:

HSYNC MXDEF (Syncer)(DWORD h, DWORD t, QWORD p, SYNCPROC *proc, void *user);

The callback function type SYNCPROC is defined as follows:

typedef void (CALLBACK SYNCPROC)(HSYNC h, DWORD c, DWORD d, void *user);

I have only been able to use the Syncer function with setting the callback argument to nil so far.

I tried creating a function outside the Class as someone suggested:

func callbackCalled(handle: HSYNC, channel: DWORD, data: DWORD, user: UnsafeMutableRawPointer) -> Void { print("callback called") }

And I even tried this method within the Class:

var callbackTest : @convention(c) (_ handle: HSYNC, _ channel: DWORD, _ data: DWORD, _ user: UnsafeMutableRawPointer) -> Void = { print("\($0) \($1) \($2) \($3)") }

But however I tried / read upon this topic I always end up with this error message:

Cannot convert value of type 'Void' (aka '()') to expected argument type '(@convention(c) (HSYNC, DWORD, DWORD, UnsafeMutableRawPointer?) -> Void)!'

My question is: How am I supposed to satisfy the criteria regarding this type of a callback function?

I haven't been able to find any info regarding these types of callback functions, presumably due to my lack of knowledge and thorough understanding of the problem. Thank you in advance!

Upvotes: 3

Views: 3324

Answers (1)

Martin R
Martin R

Reputation: 539715

Passing a global function as callback should work, but the last parameter must be an optional UnsafeMutableRawPointer?:

func callbackCalled(handle: HSYNC, channel: DWORD, data: DWORD, user: UnsafeMutableRawPointer?) -> Void {
    print("callback called")
}

Syncer(h, c, d, callbackCalled, u)

Alternatively, pass a closure expression (and let compiler infer the types of the parameters):

Syncer(h, d, c, {
    (handle, channel, data, user) in
    // ...
}, u)

You can not pass an instance method as a callback, compare How to use instance method as callback for function which takes only func or literal closure.

If NULL is not a valid value for the user parameter then you can add "Nullability annotations" to the C declaration, e.g.

typedef void (CALLBACK SYNCPROC)(HSYNC h, DWORD c, DWORD d, void * _Nonnull user);

and then it will be represented as a non-optional UnsafeMutableRawPointer in Swift.

Upvotes: 4

Related Questions