如何在swift中将回调函数传递给sqlite3_exec?

时间:2014-07-16 11:54:54

标签: list sqlite swift

如何在Swift中将回调函数传递给sqlite3_exec

sqlite_str = sqlite_str + "\(sqlite_property_str))";

var str:NSString = sqlite_str;

var sqlite:COpaquePointer = share().sqlite3_db;
var errmsg:UnsafePointer<Int8> = nil

let rc = sqlite3_exec(sqlite, str.cStringUsingEncoding(NSUTF8StringEncoding), <#callback: CFunctionPointer<((UnsafePointer<()>, Int32, UnsafePointer<UnsafePointer<Int8>>, UnsafePointer<UnsafePointer<Int8>>) -> Int32)>#>, <#UnsafePointer<()>#>, <#errmsg: UnsafePointer<UnsafePointer<Int8>>#>)

4 个答案:

答案 0 :(得分:2)

Swift 2.2为实现sqlite3_exec callback函数提供了两个选项:(1)全局,非实例func过程或(2)非捕获文字{{1闭包。

sqlite.org"SQLite in 5 minutes or less"示例在Swift Xcode7项目here中实现。

可读{}

typealias

回调方法

typealias sqlite3 = COpaquePointer
typealias CCharHandle = UnsafeMutablePointer<UnsafeMutablePointer<CChar>>
typealias CCharPointer = UnsafeMutablePointer<CChar>
typealias CVoidPointer = UnsafeMutablePointer<Void>

关闭方法

func callback(
    resultVoidPointer: CVoidPointer, // void *NotUsed 
    columnCount: CInt,               // int argc
    values: CCharHandle,             // char **argv     
    columns: CCharHandle             // char **azColName
    ) -> CInt {
    for  i in 0 ..< Int(columnCount) {
        guard let value = String.fromCString(values[i]) 
        else { continue }
        guard let column = String.fromCString(columns[i]) 
        else { continue }
        print("\(column) = \(value)")
    }
    return 0 // status ok
}

func sqlQueryCallbackBasic(argc: Int, argv: [String]) -> Int {
    var db: sqlite3 = nil 
    var zErrMsg:CCharPointer = nil
    var rc: Int32 = 0 // result code

    if argc != 3 {
        print(String(format: "ERROR: Usage: %s DATABASE SQL-STATEMENT", argv[0]))
        return 1
    }

    rc = sqlite3_open(argv[1], &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String.fromCString(sqlite3_errmsg(db))! ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(db, argv[2], callback, nil, &zErrMsg)
    if rc != SQLITE_OK {
        print("ERROR: sqlite3_exec " + String.fromCString(zErrMsg)! ?? "")
        sqlite3_free(zErrMsg)
    }

    sqlite3_close(db)
    return 0
}

答案 1 :(得分:0)

这是(目前)不可能的。 Xcode 6 beta 4发行说明:

  

但是,您无法调用C函数指针或将闭包转换为   C函数指针类型。

作为一种解决方法,您可以将sqlite3_exec与其回调放在一起 C包装函数并从Swift调用。

答案 2 :(得分:0)

我想为Swift 3和Linux的@l --marc l答案提供更新,这有助于我启动并运行。谢谢@l --marc l!

回调方法

func callback(
    resultVoidPointer: UnsafeMutablePointer<Void>?, // void *NotUsed 
    columnCount: Int32, // int argc
    values:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?, // char **argv     
    columns:UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>? // char **azColName
    ) -> CInt {
    var dic: [String:String] = [:]
    for  i in 0 ..< Int(columnCount) {
        guard let value = values?[i] 
        else { continue }
        guard let column = columns?[i] 
        else { continue }

        let strCol = String(cString:column) 
        let strVal = String(cString:value)

        dic[strCol] = strVal
        //print("\(strCol) = \(strVal)")
    }
    resultSet.append(dic)
    return 0 // status ok
}

func sqlQueryCallbackBasic(dbStr:String, query:String) -> Int {
    var db: OpaquePointer? 
    var zErrMsg:UnsafeMutablePointer<Int8>?
    var rc: Int32 = 0 // result code

    rc = sqlite3_open(dbStr, &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String(sqlite3_errmsg(db)) ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(db, query, callback, nil, &zErrMsg)
    if rc != SQLITE_OK {
        let errorMsg = zErrMsg
        print("ERROR: sqlite3_exec \(errorMsg)")
        sqlite3_free(zErrMsg)
    }

    sqlite3_close(db)
    return 0
}

关闭方法

func sqlQueryClosureBasic(dbStr:String, query:String) -> Int {
    var db: OpaquePointer? 
    var zErrMsg:UnsafeMutablePointer<Int8>?
    var rc: Int32 = 0

    rc = sqlite3_open(dbStr, &db)
    if  rc != 0 {
        print("ERROR: sqlite3_open " + String(sqlite3_errmsg(db)) ?? "" )
        sqlite3_close(db)
        return 1
    }

    rc = sqlite3_exec(
        db,      // database 
        query, // statement
        {        // callback: non-capturing closure
            resultVoidPointer, columnCount, values, columns in
            var dic: [String:String] = [:]
            for i in 0 ..< Int(columnCount) {
                guard let value = values?[i]
                else { continue }
                guard let column = columns?[i]
                else { continue }

                let strCol = String(cString:column)
                let strVal = String(cString:value)

                dic[strCol] = strVal

                //print("\(strCol) = \(strVal)")
            }
            resultSet.append(dic)
            return 0
        }, 
        nil, 
        &zErrMsg
    )

    if rc != SQLITE_OK {
        let errorMsg = zErrMsg
        print("ERROR: sqlite3_exec \(errorMsg)")
        sqlite3_free(zErrMsg)
    }
    sqlite3_close(db)
    return 0
}

<强>测试

import Glibc

var resultSet: [[String: String]] = [[:]]

//sqlQueryClosureBasic(dbStr:"Sqlite_Test.db", query:"SELECT * FROM Employee")

sqlQueryCallbackBasic(dbStr:"Sqlite_Test.db", query:"SELECT * FROM Employee")


for row in resultSet {
    for (col, val) in row {
        print("\(col): \(val)")
    }
}

答案 3 :(得分:0)

那个 C 函数的第一个参数(sqlite3_exec 的第三个参数)是你传递给 sqlite3_exec 的第四个参数的东西。因此,可以将 Swift 闭包传递给 sqlite3_exec

// Define a Type for later loading in the C function
typealias RowHandler = (
    Int32,
    UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
    UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
) -> Void

// The Swift closure to pass into sqlite3_exec
var rowHandler: RowHandler = { count, names, values in
    // Do anything you want
}

let state = withUnsafeMutablePointer(to: &rowHandler) { rowHandlerPointer in
    
    return sqlite3_exec(
        connection,
        "SOME SQLITE STATEMENT",
        { rowHandlerRawPointer, columnCount, values, columns in
            
            // Load the pointer as the Type of the closure
            let rowHandler = rowHandlerRawPointer!.load(as: RowHandler.self)
            
            // Use it!
            rowHandler(columnCount, columns, values)
        },
        rowHandlerPointer, // This will become rowHandlerRawPointer in the C function
        nil
    )
}

因为 RowHandler 是一个 Swift 闭包类型,我们可以将它作为包装方法的参数:

class SQLiteDatabase {
    
    // Define a closure type for later loading in the C function
    typealias RowHandler = (
        Int32,
        UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?,
        UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>?
    ) -> Void
        
    var connection: OpaquePointer?

    // Other methods...
    
    func execute(_ statement: String, receiveRow: @escaping RowHandler) -> Int32 {
        
        
        // The Swift closure to pass into sqlite3_exec
        var rowHandler = receiveRow
        
        let state = withUnsafeMutablePointer(to: &rowHandler) { rowHandlerPointer in
            
            return sqlite3_exec(
                connection,
                statement,
                { rowHandlerRawPointer, columnCount, values, columns in
                    
                    // Load the pointer as the Type of the closure
                    let rowHandler = rowHandlerRawPointer!.load(as: RowHandler.self)
                    
                    // Use it!
                    rowHandler(columnCount, columns, values)
                },
                rowHandlerPointer, // This will become rowHandlerRawPointer in the C function
                nil
            )
        }
        
        return state
    }
}

用法:

func result(database: SQLiteDatabase) -> [[String: String]] {
    
    
    var rows = [[String: String]]()
    _ = database.execute("SOME STATEMENT") { (count, names, values) in
        var row = [String: String]()
        for index in 0..<Int(count) {
            guard let name = names?[index], let value = values?[index] else { continue }
            row[String(cString: name)] = String(cString: value)
        }
        rows.append(row)
    }
    return rows
}