I'm working on a Swift 3 wrapper for the libxml2 C-library.
There are two convenience methods to convert String
to UnsafePointer<xmlChar>
and vice versa. In libxml2 xmlChar
is declared as unsigned char
.
UnsafePointer<xmlChar>
to String
is uncomplicated
func stringFrom(xmlchar: UnsafePointer<xmlChar>) -> String {
let string = xmlchar.withMemoryRebound(to: CChar.self, capacity: 1) {
return String(validatingUTF8: $0)
}
return string ?? ""
}
For String
to UnsafePointer<xmlChar>
I tried many things for example
let bytes = string.utf8CString.map{ xmlChar($0) }
return UnsafePointer<xmlChar>(bytes)
but this doesn't work, the only working solution I figured out is
func xmlCharFrom(string: String) -> UnsafePointer<xmlChar> {
let pointer = (string as NSString).utf8String
return unsafeBitCast(pointer, to: UnsafePointer<xmlChar>.self)
}
Is there a better, swiftier way without the bridge cast to NSString
and unsafeBitCast
?
Swiftiest way I can think of is to just use the bitPattern:
initializer:
let xmlstr = str.utf8CString.map { xmlChar(bitPattern: $0) }
This will give you an Array
of xmlChar
s. Hang onto that, and use Array
's withUnsafeBufferPointer
method when you need to pass an UnsafePointer
to something:
xmlstr.withUnsafeBufferPointer { someAPIThatWantsAPointer($0.baseAddress!) }
Don't let the UnsafePointer
escape from the closure, as it won't be valid outside it.
EDIT: How's this for a compromise? Instead of having your function return a pointer, have it take a closure.
func withXmlString<T>(from string: String, handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
let xmlstr = string.utf8CString.map { xmlChar(bitPattern: $0) }
return try xmlstr.withUnsafeBufferPointer { try handler($0.baseAddress!) }
}
Or, as an extension on String
:
extension String {
func withXmlString<T>(handler: (UnsafePointer<xmlChar>) throws -> T) rethrows -> T {
let xmlstr = self.utf8CString.map { xmlChar(bitPattern: $0) }
return try xmlstr.withUnsafeBufferPointer { try handler($0.baseAddress!) }
}
}