要求关联类型在@convention(c)块中可表示

mar*_*rux 6 swift

我想有一种通用的方式来做类似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 */ }
}
Run Code Online (Sandbox Code Playgroud)

所以我希望它看起来像这样:

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 */ }
}
Run Code Online (Sandbox Code Playgroud)

但是,我得到了错误:

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

我可以在输入/输出关联类型上放置任何约束,以允许我声明FunctionalBlock的通用形式吗?如果没有@convention(c),它可以正常工作,但是我需要它才能形成C函数调用。

Cri*_*tik 0

目前这在 Swift 中是不可能的,因为 Swift 管理作为协议传递的值的方式,并且它CVarArg是一个协议。

幕后发生的事情是,当在协议的保护下传递值时,Swift 编译器会创建一个包装该值的存在容器,该值在被调用方站点透明地解开包装。

所以基本上你的块实际上看起来像这样:

typealias FunctionalBlock = @convention(c) (Container<In>) -> Container<Out>
Run Code Online (Sandbox Code Playgroud)

由于这种幕后转换,您没有传递可以用 C 表示的值,因此会出现错误。

这与其他协议相关问题非常相似,例如著名的协议不符合自身?

最好的选择是为符合 CVarArg 的所有类型添加重载,因为这是一个有限且不可更改的列表。