39

After migrating to Swift 2, I am getting this issue with an error stating that I should now use @convention(c) (T) -> U. I've tried permutations but so far no luck.

func foo(context: AnyObject?, width: CGFloat) -> Int {

}

let bar = unsafeBitCast(foo, CFunctionPointer<(UnsafeMutablePointer<Void>, Float) -> Int>.self)
Ian
  • 12,538
  • 5
  • 43
  • 62
John Difool
  • 5,572
  • 5
  • 45
  • 80
  • I think it should be possible to avoid the unsafeBitCast completely. Can you provide some more information? How is the C function declared, what is passed as the context object, ... ? – Martin R Jun 10 '15 at 02:35
  • This may also be helpful for passing object pointers through C callbacks: http://stackoverflow.com/questions/30786883/swift-2-unsafemutablepointervoid-to-object. – Martin R Jun 12 '15 at 17:50
  • `CGFloat` is not the same as `Float`.  On 64-bit platforms _(i.e. every Apple device in the last few years)_ it's a `Double` and on 32-bit platforms it's a `Float`.  However, in Objective-C `CGFloat` is just a plain-C `typedef`, so you should just be using `CGFloat` here. – Slipp D. Thompson Jan 27 '17 at 06:19

2 Answers2

33

Passing a Swift closure to a C function taking a function pointer parameter is now supported in Swift 2, and, as you noticed, function types are specified with the @convention(c) attribute.

If you pass a closure directly as an argument to the C function then this attribute is inferred automatically.

As a simple example, if you have this C function

CGFloat myCFunction(CGFloat (callback)(CGFloat x, CGFloat y)) {
    return callback(1.1, 2.2);
}

then you can call it from Swift as

let result = myCFunction( {
    (x, y) -> CGFloat in
    return x + y
} )
print(result) // 3.3

which does exactly the same as the more verbose

let swiftCallback : @convention(c) (CGFloat, CGFloat) -> CGFloat = {
    (x, y) -> CGFloat in
    return x + y
} 

let result = myCFunction( swiftCallback )
print(result) // 3.3
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • How to do this with char array? – Jason Foglia Jul 05 '16 at 18:46
  • Could be wrong, but I thought it was bad practice to pass a closure to a c function because the address to the function (i.e. its pointer) isn't stable. It has to be a static member on a class/struct or a global function, no? – Mark A. Donohoe Mar 29 '23 at 12:54
  • @MarkA.Donohoe: I am fairly sure that this is no problem. Only functions/closures that do not capture context can be marked with @convention(c) and then they use the C calling convention. The ability to pass such functions/closures as a “callback function” to C was explicitly implemented in Swift 2. Unfortunately I cannot find the old release notes anymore, otherwise I would try to provide a reference. – Martin R Mar 29 '23 at 13:16
22

You no longer need to create a CFunctionPointer in Swift 2. Instead, you can annotate your type by calling convention, in this case c, and use it directly.

typealias CFunction = @convention(c) (UnsafeMutablePointer<Void>, Float) -> Int
let bar = unsafeBitCast(foo, CFunction.self)

The relevant bits of the @convention description in the Type Attributes section of The Swift Programming Language are:

The c argument is used to indicate a C function reference. The function value carries no context and uses the C calling convention.

Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
  • 3
    Thanks. Simultaneously, I got it to work with let bar = foo as @convention(c) (AnyObject?, CGFloat) -> Int – John Difool Jun 09 '15 at 19:10
  • 2
    Er, @convention(c) (UnsafeMutablePointer?, CGFloat) -> Int because AnyObject? isn't convertible apparently. I had to unsafebitcast in my called method signature too. – John Difool Jun 09 '15 at 19:20
  • What does .self mean when called directly from a type like CFunction.self ? – Pop Flamingo May 29 '16 at 21:06
  • Beware, do not unsafe bitcast between function types. The point of `@convention(c)` is that it changes the calling convention for the function to which it is applied. By bitcasting, you prevent the type system from diagnosing problems, but you don't actually change the way the way the function processes its arguments -- so you'll be left with a broken function that doesn't speak the same language as its callers. (This is especially true for functions with AnyObject parameters.) – Karoy Lorentey Aug 07 '21 at 20:17