Swift2 @convention(c) 带有 char* 和 type**

Swift2 @convention(c) with char* and type**

我正在尝试编写指向 c 方法的指针:

//c signature:
typedef void (*startDocumentSAXFunc) (void *ctx);

//Swift2 working code 
let startDocument: @convention(c) (UnsafeMutablePointer<Void>) -> Void  = { (ctx) -> Void in
    NSLog("Start Document")

}
    //c signature
    typedef void (*startElementSAXFunc) (void *ctx,
                const xmlChar *name,
                const xmlChar **atts);

    //Swift2 code
    let startElement: @convention(c) (UnsafeMutablePointer, UnsafePointer, ???) -> Void  = { (ctx, name, attributes) -> Void in
        NSLog("Start Element \(name), \(attributes)")
    }
    //c signature
    typedef void (XMLCDECL *errorSAXFunc) (void *ctx,
                const char *msg, ...);

    //Swift2
     let error: @convention(c) (UnsafeMutablePointer, ???) -> Void =
    { (ctx, msg) -> Void in
        NSLog("Error \(msg)")
    }

我尝试了类型 UnsafePointer 但它不起作用。

这里的目的是能够使用比 NSXML 库更快的 libXML。

感谢您的帮助!

检查此 'self explanatory' 示例

let str = "A"
var c = str.utf8.map { (c) -> CChar in
    return CChar(c)
}[0]
print(c, c.dynamicType)  // 65 Int8

var p = UnsafeMutablePointer<CChar>.alloc(1)
print(p.memory, p.memory.dynamicType) // 0 Int8
p.memory = 65

func modify(pp: UnsafeMutablePointer<UnsafeMutablePointer<CChar>>)->Void {
    print(pp.memory.memory, pp.memory.memory.dynamicType)
}

let pp = withUnsafeMutablePointer(&p){ [=10=] }
print(pp, pp.dynamicType) // 0x0000000119bbf750 UnsafeMutablePointer<UnsafeMutablePointer<Int8>>

modify(pp) // 65 Int8

...

p.dealloc(1)
modify(pp) // -107 Int8
// -107 is some 'random' value, becase the memory there was dealocated!!

UnsafePoiner<CChar>const char *UndafeMutablePointer<CChar>char * 等...

C函数类型

void (*startElementSAXFunc) (void *ctx,
            const xmlChar *name,
            const xmlChar **atts);

映射到 Swift 为

(UnsafeMutablePointer<Void>, UnsafePointer<xmlChar>, UnsafeMutablePointer<UnsafePointer<xmlChar>>) -> Void

所以有效的 startElementSAXFunc

func onStartElement(ctx: UnsafeMutablePointer<Void>,
    name: UnsafePointer<xmlChar>,
    atts: UnsafeMutablePointer<UnsafePointer<xmlChar>>) {

    // ... 
}

并且可以在没有任何转换的情况下分配给处理程序:

var handler = xmlSAXHandler()
handler.initialized = XML_SAX2_MAGIC
// ...
handler.startElement = onStartElement

现在 xmlCharunsigned char 的类型别名,即 UInt8 在 Swift 中,与 CChar 又名 Int8 不同,因此 如果转换传递的字符,则需要额外的强制转换 到 onStartElement:

中的 Swift 字符串
    let sName = String.fromCString(UnsafePointer(name))!
    print("start element, name = ", sName)

atts是指向字符指针数组的指针,而你 可以像在 C:

中一样遍历该数组
    if atts != nil {
        var ptr = atts
        while ptr[0] != nil && ptr[1] != nil {
            let key = String.fromCString(UnsafePointer(ptr[0]))!
            let value = String.fromCString(UnsafePointer(ptr[1]))!
            print("attr:", key, "=", value)
            ptr += 2
        }
    }

以类似的方式,您可以实现 endElementSAXFunccharactersSAXFunc.


但是errorSAXFunc有一个大问题: 该函数使用可变参数列表,因此不能 在 Swift 中实现,所以你在这里运气不好。

以下编译并且似乎到运行:

let onError : @convention(c) (UnsafeMutablePointer<Void>, UnsafePointer<xmlChar>)->Void = {
        (ctx, msg) in

        let sMsg = String.fromCString(UnsafePointer(msg))!
        print("error:", sMsg)
}

handler.error = unsafeBitCast(onError, errorSAXFunc.self)

但据我所知,这是未定义的行为。 这也没有帮助,因为您会得到错误格式字符串 仅(在我的测试用例中只是 %s)。