October 22, 2014
Swift, C libraries, and Mapping Swift types to C pointer types
In my last posting I described a swift library, SwiftlyDB, I wrote to insert data into a SQLite database using the sqlite3 library. I promised some ‘lessons learned’ postings about what I picked up during the creation of that library. This is the first of those postings.
In Swift and Objective-C SQLite is accessed using a C library. This means a whole series of pointers are needed to move data back and forth between Swift and C. Thankfully Xcode will handle most, but not all, required Swift-to-C pointer conversions for you. When it doesn’t You will need to get used to COpaquePointer, UnsafePointer, and UnsafeMutablePointer. These Swift types represent C pointers, const pointers, and pointer pointers (**) respectively.
COpaquePointers represent pointers to any non-standard C data type. They can be a simple pointer or a pointer to an array, but they are not used to point to int, char, double, and other primitive types. When creating a Swift variable that refers to a sqlite instance, a COpaquePointer is used since a sqlite3 pointer is not a primitive type such as int or char. I assume the Swift pointer is called an opaque pointer since there is no way to tell what kind of C construct it represents just by looking at it. If you can’t see through glass to what is behind it, the glass is opaque, right?
One result of calling sqlite3_open is the population of a COpaquePointer with the sqlite instance. This is done by passing the opaque pointer to the sqlite3_open function. The following two-line example shows a pointer being created and passed to the that function.
var theDB:COpaquePointer = nil sqlite3_open((fileLocation as NSString). cStringUsingEncoding(NSUTF8StringEncoding), &theDB)
Not so bad really. There is that & operator confusing the situation, and the conversion of an NSString to a C string to muddy the waters, but all in all, not too bad….until you look at the signature for sqlite3_open function.
Swift function signature – sqlite3_open(filename: UnsafePointer<Int8>,
ppDb: UnsafeMutablePointer<COpaquePointer>) -> Int
C function signature – int sqlite3_open(const char *filename, sqlite3 **ppDb )
Now things look much messier. The second parameter in the Swift signature seems to be a mess. The clue to figuring out what it means is the parameter name, ppDb. ppDb stands for Database pointer pointer. So the UnsafeMutablePointer<COpaquePointer> declaration must match up with the sqlite3** type. In the case of sqlite3_open, and in many other C functions, pointer pointers (**) are used to retrieve pointers to items generated during the function’s execution that are not returned. The UnsafeMutablePointer name was selected to reflect the in-and-out nature of the C pointers represented by UnsafeMutablePointers. The pointer is unsafe since it might be null, and it is mutable since what the pointer is pointing at can change (it starts out pointing at null and can end up pointing at a swift3 instance). Swift provides the & operator to generate UnsafeMutablePointers from COpaquePointers. Don’t try to instantiate one yourself. Using the init method of UnsafeMutablePointer didn’t work at the time I wrote the library.
The sibling to the changeable UnsafeMutablePointer is the unchangeable UnsafePointer. In C terminology these are constant (const) pointers. The value of what they pointing at can be changed, but they can not be changed to point at a different memory location. The first parameter of sqlite3_open, seen in the Swift function signature above, is an UnsafePointer to an 8bit int. This must match the C parameter type of const char *. That is confusing until you understand a C character is the same thing as a C 8bit int.
When dealing with any C library, keeping these mappings between C and Swift pointer types in mind makes Swift-to-C library calls much easier.