6

I want to have a generic way of doing something like in Swift 3:

public protocol Callable {
    associatedtype In : CVarArg
    associatedtype Out : CVarArg
}

public struct IntCallable : Callable {
    public typealias In = Int
    public typealias Out = Double

    public typealias FunctionalBlock = @convention(c) (In) -> Out

    public func call(_ block: FunctionalBlock) { /* do stuff */ }
}

So I'd like it to look more like this:

public protocol Callable {
    associatedtype In : CVarArg
    associatedtype Out : CVarArg
    typealias FunctionalBlock = @convention(c) (In) -> Out
}

public struct IntCallable : Callable {
    public typealias In = Int
    public typealias Out = Double
}

public extension Callable {
    public func call(_ block: FunctionalBlock) { /* do stuff */ }
}

However, I get the error:

'(Self.In) -> Self.Out' is not representable in Objective-C, so it cannot be used with '@convention(c)'

Is there any constraint I can place on the In/Out associatedtypes that will allow my to declare the generic form of the FunctionalBlock? It works fine without @convention(c), but I need it in order to form a C function call.

marcprux
  • 9,845
  • 3
  • 55
  • 72

1 Answers1

0

This is not currently possible in Swift, due to the how Swift manages values passed as protocols, and CVarArg is a protocol.

What happens behind the scenes is that when passing a value under the umbrella of a protocol, the Swift compiler creates an existential container that wraps the value, a value which is transparently unwrapped at the callee site.

So basically your block actually looks something like this:

typealias FunctionalBlock = @convention(c) (Container<In>) -> Container<Out>

Due to this behind-the-scenes transform, you're not passing values that can be represented in C, thus the error you get.

This is very similar to other protocol related issues, like the famous Protocol doesn't conform to itself?.

Your best bet would be to add overloads for all types that conform to CVarArg, since this is a finite and unchangeable list.

Cristik
  • 30,989
  • 25
  • 91
  • 127