HOME

Implementing a C Callback with a Void Pointer Parameter in Swift

Swift is a promising programming language, but it is brand new, and when coding in Swift we are likely to have to use legacy code written in other languages, in particular C.

What if we need to interact with a C API that involves callbacks that take void pointer parameters? We may want to implement these callbacks in Swift and have the legacy C code call them. In this article we use Swift 2 features and look at two cases:

This tutorial assumes a basic knowledge of Xcode as well as Swift and C programming.

Sample C API

This tutorial uses the following trivial C API: /** * The struct to be passed to the callback via void* */ typedef struct { int32_t m_Int; int64_t m_Long; int16_t m_Array[3]; } APIStruct; /** * Function pointer type for the callback. Callback receives a void * pointer, which is then treated as APIStruct * in the callback. */ typedef void (*my_cb_t)( void * ); /** * C function using the callback. It creates an instance of APIStruct, prints * it out, and gives it via void pointer to the callback identified by the 1st * parameter. If the 2nd parameter is !=0, then the struct is also printed out * after the callback returns. */ int CUseCallback( my_cb_t, int ); You can find the complete source code of this example on github.

Implementing the Callback in Swift 2

Create an Xcode Project

First we create an Xcode project, using the OS X Application/Command Line Tool template and Swift as the language. The newly created project now contains a single file, main.swift. We then add two more files: When we add the C source file, c_code.c, Xcode offers to create a bridging header. We decline because we want to use bridging.h that we add to the project manually. Alternatively we could have used the Xcode-generated bridging header and included it in c_code.c. Specify the bridging header in the Swift Compiler - Code Generation section under Build Settings:
Specifying the bridging header.
The project should now look something like this:
Screenshot of the Xcode project.

Invoke C API

Our C API can be called from Swift because Swift imports function prototypes and type definitions found in the bridging header that looks as follows: #ifndef bridging_h #define bridging_h #pragma pack(1) // pack(2) & pack(4) would have the same effect // NaiveCallback() works with no pragma or with pack(8) #include <stdlib.h> typedef struct { int32_t m_Int; int64_t m_Long; int16_t m_Array[3]; } APIStruct; typedef void (*my_cb_t)( void * ); int CUseCallback( my_cb_t, int ); #endif /* bridging_h */ Please note the pragma at the top of the file. Packing has to do with how data is aligned within a structure. See, for example, GCC documentation. If the pragma were not there, then even the naive approach, see below, would have worked fine. However, it is quite likely that packing used in a real-world C library would be different from the one used by default in Xcode, and then we would run into problems with the naive approach.

Now we need to find out how the C definitions found in the bridging header are imported into Swift. To see how CUseCallback() is imported, just start typing the function name in main.swift, and you will see the following, thanks to Xcode's code completion:
How a function is imported into Swift; use code completion.
Alternatively, one can click on the Quick Help Inspector button in Xcode, which is a small circle with a question mark in the Utilities area, and then place the cursor in the function name to see how it is imported:
How a function is imported into Swift; use Quick Help Inspector.
Using one of these methods we find that our C API is imported into Swing as follows: // APIStruct: struct APIStruct { var m_Int : Int32 var m_Long : Int64 var m_Array : (Int16, Int16, Int16) } // Callback function pointer type: typealias my_cb_t = (UnsafeMutablePointer<Void>) -> Void // C function that uses the callback: func CUseCallback(_: my_cb_t!, _: Int32) -> Int32 Please note that the array member of the C struct is imported as a tuple.

Naive Approach: Manually Implement a Swift Structure

Armed with this knowledge, we define a Swift structure, NaiveStruct, matching the imported APIStruct, define a callback that populates NaiveStruct from the void pointer, and also define a helper function that prints out a NaiveStruct: /** * This is a naive native Swift structure that tries to mimic the APIStruct * from the C API. Depending on structure packing in C code, this may or * may not work. */ struct NaiveStruct { var m_Int : Int32 var m_Long : Int64 var m_Array : (Int16, Int16, Int16) } /** * Prints an instance of NaiveStruct. Only selected fields are printed. */ func printNaive( s : NaiveStruct ) { print( "Printing NaiveStruct: " ) print( " m_Long: \(s.m_Long)" ) print( " m_Array: \(s.m_Array.0) \(s.m_Array.1) \(s.m_Array.2) " ) } /** * This is a naive implementation of the callback. Casts the void *, * provided via an argument of type UnsafeMutablePointer<Void>, to * UnsafeMutablePointer<NativeStruct>, takes its memory and naively * creates an instance of NaiveStruct from it. * * This may or may not work depending on how APIStruct is packed in C * code. If, e.g., #pragma pack(2) is used in C code, the content of * NaiveStruct won't match that of the APIStruct provided by the C * code via the void *. */ let NaiveCallback : my_cb_t = {( p : UnsafeMutablePointer<Void> )->Void in print( "In NaiveCallback(), received a void pointer. " ) let _ns : NaiveStruct = (UnsafeMutablePointer<NaiveStruct>(p)).memory; printNaive( _ns ); } /** * Call the C API giving it our NaiveCallback. */ CUseCallback( NaiveCallback, 0 ) The output from using this callback is: Entered C code, printing newly created structure: Printing structure in C m_Long = 4567890123 m_Array = 123 456 789 In NaiveCallback(), received a void pointer. Printing NaiveStruct: m_Long: 128353117661036545 m_Array: 789 0 0 As can be easily seen, the NaiveStruct instance obtained in this way from the void pointer is corrupted. It would actually be correct if the pack pragma was not used. However, a real world C framework is likely to use a structure alignment that would cause problems with this approach.

Use Imported Struct to Treat the Pointer as an IN Parameter

Now let's use APIStruct imported into Swift via the bridging header: /** * Dumps an instance of the C API's structure, APIStruct, imported via * the bridging header. */ func printAPI( s : APIStruct ) { print( "Printing APIStruct: " ) print( " m_Long: \(s.m_Long)" ) print( " m_Array: \(s.m_Array.0) \(s.m_Array.1) \(s.m_Array.2) " ) } /** * A better callback implementation. * * This one casts the void pointer to UnsafeMutablePointer<APIStruct>, * then uses its memory to construct an instance of APIStruct. Even if * tight packing is used by the C code, the APIStruct will correctly * reflect the data placed there by the C code. This is as long as * the pack pragma is available via the bridging header. * * It is called OneWayCallback because whatever changes it makes to * the APIStruct provided to it by C code won't be visible to the * C code. This is because we modify a copy of the structure * populated by the C code. */ let OneWayCallback : my_cb_t = {( p : UnsafeMutablePointer<Void> )->Void in print( "In OneWayCallback(), received a void pointer. " ); var _apiS : APIStruct = (UnsafeMutablePointer<APIStruct>(p)).memory printAPI ( _apiS ); print( "Setting m_Long in the structure to 98765432109 " ) _apiS.m_Long = 98765432109 } /** * Call the C API giving it the 1-way callback. */ CUseCallback( OneWayCallback, 1 ) Here the output: Entered C code, printing newly created structure: Printing structure in C m_Long = 4567890123 m_Array = 123 456 789 In OneWayCallback(), received a void pointer. Printing APIStruct: m_Long: 4567890123 m_Array: 123 456 789 Setting m_Long in the structure to 98765432109 Now we are back in C code, see if the callback changed the structure... Printing structure in C m_Long = 4567890123 m_Array = 123 456 789 Now the data sent by the C code to the Swift callback via the void * parameter has been read correctly. However, changes made in the callback to the received APIStruct did not make it back to the C code because what was modified was a copy of the APIStruct passed via the void pointer.

Implement a Wrapper Swift Structure to Treat the Pointer as an INOUT Parameter

Here is a different approach, in which changes made to the data passed via the void pointer are visible to the C code invoking the callback, but the downside is that the data is valid in Swift only for as long as it is valid in the C framework. Another advantage of this approach is that the wrapper does not have to mimic the structure imported from C. The properties for accessing the passed APIStruct can be named differently, and other properties and methods can be added. It doesn't have to be a struct, either; it might just as well be a class. /** * A structure that wraps an UnsafeMutablePointer<APIStruct> and * has getter/setter properties to access the data in the APIStruct * pointed to by the wrapped pointer. Because the wrapped thing is * not a copy of, but a pointer to the struct provided by the C code, * any changes made via the computed properties will be available * when the control returns to the C code that invoked the callback. * The downside, however, is that the properties will work correctly * only for the duration of the lifetime of the structure in the C * code. * * In this case the struct mimics the APIStruct, but it doesn't have * to. It could be a Swing class with plenty of added functionality. */ struct WrapperStruct { var m_Pointer : UnsafeMutablePointer<APIStruct> init( _p : UnsafeMutablePointer<APIStruct> ) { m_Pointer = _p } var m_Int : Int32 { get { return m_Pointer.memory.m_Int } set( val ) { m_Pointer.memory.m_Int = val } } var m_Long : Int64 { get { return m_Pointer.memory.m_Long } set( val ) { m_Pointer.memory.m_Long = val } } var m_Array : (Int16, Int16, Int16) { get { return (m_Pointer.memory.m_Array.0, m_Pointer.memory.m_Array.1, m_Pointer.memory.m_Array.2) } set ( val ) { m_Pointer.memory.m_Array.0 = val.0 m_Pointer.memory.m_Array.1 = val.1 m_Pointer.memory.m_Array.2 = val.2 } } } /** * Dumps selected fields from an instance of WrapperStruct. */ func printWrapper( s : WrapperStruct ) { print( "Printing APIStruct: " ) print( " m_Long: \(s.m_Long)" ) print( " m_Array: \(s.m_Array.0) \(s.m_Array.1) \(s.m_Array.2) " ) } /** * A callback implementation that casts the argument to * UnsafeMutablePointer<APIStrunct> and wraps that in a WrapperStruct. * Please note that changes made via the WrapperStruct properties * are visible to the C code! * * Notice that here we use a top-level Swift function instead of a closure * literal. Swift 2.1 documentation states that top-level functions can be * passed where a function pointer parameter is required. We could have used * a literal as well. */ func TwoWayCallback( p : UnsafeMutablePointer<Void> )->Void { print( "In TwoWayCallback(), received a void pointer. " ); var _wS : WrapperStruct = WrapperStruct(_p: (UnsafeMutablePointer<APIStruct>(p))) printWrapper( _wS ) print( "Setting m_Long in the structure to 98765432109 " ) _wS.m_Long = 98765432109 print( "Setting the array to 111, 222, 333" ) _wS.m_Array.0 = 111 _wS.m_Array.1 = 222 _wS.m_Array.2 = 333 } /** * Call C code with our more sophisticated 2-way callback. */ CUseCallback( TwoWayCallback, 1 ) The output from this one is: Entered C code, printing newly created structure: Printing structure in C m_Long = 4567890123 m_Array = 123 456 789 In TwoWayCallback(), received a void pointer. Printing APIStruct: m_Long: 4567890123 m_Array: 123 456 789 Setting m_Long in the structure to 98765432109 Setting the array to 111, 222, 333 Now we are back in C code, see if the callback changed the structure... Printing structure in C m_Long = 98765432109 m_Array = 111 222 333

Please feel free to contact me with questions or comments.

© 2015 swiftprogrammer.info