Interacting with C Pointers in Swift. Part 3: using CFunctionPointer

Peter Wood in Swift
17 Sep 2014, 12:20

Hi there!

In one of my previous posts (see Interacting with C Pointers in Swift. Part 2), I told you how the work with pointers in Swift is organized, and I also mentioned CFunctionPointer type. Let’s scrutinize this type today and see why we actually need it.

In “Using Swift with Cocoa and Objective-C. Interacting with C APIs” doc, CFunctionPointer type is described in few words only:

C function pointers are imported into Swift as CFunctionPointer, where Type is a Swift function type. For example, a function pointer that has the type int (*)(void) in C is imported into Swift asCFunctionPointer<() -> Int32>.

The same way as pointers to variables let us get values of variables they are pointing to, pointers to functions let calling the corresponding functions.

Let’s see the example of using a pointer to C-functions in Swift.

Suppose we have C-file in which getNextRandomValue function is defined, which returns pseudo-random number ranging from 1 to 100:

int getNextRandomValue() {      srand(time(NULL));      int randomNumber = rand() % 100 + 1;      printf("New random number: %i\n",randomNumber);      return randomNumber;  }  

The pointer to this function in C would have int (*)(void) type, while in Swift it will have CFunctionPointer<() -> Int32> type. To create CFunctionPointer, COpaquePointer is needed, e.g.:

let pointer = UnsafeMutablePointer<() -> Int32>.alloc(1)  pointer.initialize(getNextRandomValue)  let cPointer = COpaquePointer(pointer)  let functionPointer = CFunctionPointer<() -> Int32>(cPointer)  

To call a function via a pointer to it, do the following:

let newCPointer = COpaquePointer(functionPointer)  let newPointer = UnsafeMutablePointer<() -> Int32>(newCPointer)  let rNumber = newPointer.memory()  

Ok. Now let’s turn our eyes to callback functions. Pointers to functions are valued as they can be passed as parameter to other functions. A function passed to another function for calling as pointer is called a callback function. Suppose, we need to track when new disk or partition is created.

Let’s create Disk Arbitration session, register DADiskAppearedCallback and pass the object for handling:

func gotDisk(disk: DADisk!, context: UnsafeMutablePointer) {      NSLog("Got new disk: \(DADiskGetBSDName(disk))")  }    …  var session = DASessionCreate(kCFAllocatorDefault).takeRetainedValue()  let p = UnsafeMutablePointer<((DADisk!, UnsafeMutablePointer) -> Void)>.alloc(1)  p.initialize(gotDisk)  let cp = COpaquePointer(p)  let fp = DADiskAppearedCallback(cp)  DARegisterDiskAppearedCallback(      session,      kDADiskDescriptionMatchVolumeMountable.takeRetainedValue(),      fp,      nil)  /* Schedule a disk arbitration session. */  DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)  ...  

On handling stage we will get EXC_BAD_ACCESS exception.

Let’s see what caused the exception. We’ll create our own C-function, which takes pointer of void f(void) type:

void executeFunction(void(*f)(void)) {      f();  }  

Call this function from Swift, passing a pointer to greeting function as parameter:

func greeting() {      NSLog("Hello from Swift")  }    ...  let p = UnsafeMutablePointer<()->()>.alloc(1)  p.initialize(greeting)  let cp = COpaquePointer(p)  let fp = CFunctionPointer<()->()>(cp)  executeFunction(fp)  ...  

On handling stage we’ll get EXC_BAD_ACCESS exception again.

In debugger we see that instead of greeting function address, CFunctionPointer address is returned to executeFunction method:




Thus, it seems to be impossible to call a function via CFunctionPointer.

Hope this problem will be resolved in the nearest future. I’ll keep you updated.