JetForMe
(Rick M)
1
I still have a very hard time working with C. In this case, I’d like to call a SQLite function that sometimes allocates an error string internally and passes it back via char** parameter:
int sqlite3_exec(
sqlite3*, /* An open database */
const char *sql, /* SQL to be evaluated */
int (*callback)(void*,int,char**,char**), /* Callback function */
void *, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
public
func
sqlite3_exec(_: OpaquePointer!,
_ sql: UnsafePointer<Int8>!,
_ callback: (@convention(c) (UnsafeMutableRawPointer?,
Int32,
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?,
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>?) -> Int32)!,
_: UnsafeMutableRawPointer!,
_ errmsg: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!) -> Int32
This string is freed with a call to void sqlite3_free(void*);
How can I get this as a Swift String?
Also, Xcode 12.3 gives this helpful diagnostic (obviously that code won’t work, was just experimenting and noticed the diagnostic):
glessard
(Guillaume Lessard)
2
errMsg wants to be a place where sqlite3_exec can write back a pointer.
func exec(sql inSQL: String) throws
{
var errMsg: UnsafeMutablePointer<Int8>? = nil
let result = sqlite3_exec(nil, inSQL, nil, nil, &errMsg)
if result != 0
{
let message = errMsg.map({ String(cString: UnsafeRawPointer($0).assumingMemoryBound(to: UInt8.self)) }) ?? ""
// call sqlite3_free here
try throwIfNotOK(result, message)
}
}
Unfortunately Int8 is not particularly comfortable to work with for strings, but there you are.
1 Like
JetForMe
(Rick M)
3
Wow. I could’ve sworn that was the first thing I tried, but maybe not. Thanks!
glessard
(Guillaume Lessard)
4
Using withMemoryRebound would be better; the earlier is probably fine for this particular case, but isn't the general solution.
let message = errMsg.map { chars -> String in
let count = strlen(chars)
let message = chars.withMemoryRebound(to: UInt8.self, capacity: count) { String(cString: $0) }
sqlite3_free(chars)
return message
}
2 Likes