Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Swift wrappers for C functions taking char** arguments
// Usage
let argv = CStringArray(["ls", "/"])
posix_spawnp(nil, argv.pointers[0], nil, nil, argv.pointers, nil)
// Is this really the best way to extend the lifetime of C-style strings? The lifetime
// of those passed to the String.withCString closure are only guaranteed valid during
// that call. Tried cheating this by returning the same C string from the closure but it
// gets dealloc'd almost immediately after the closure returns. This isn't terrible when
// dealing with a small number of constant C strings since you can nest closures. But
// this breaks down when it's dynamic, e.g. creating the char** argv array for an exec
// call.
class CString {
private let _len: Int
let buffer: UnsafeMutablePointer<Int8>
init(_ string: String) {
(_len, buffer) = string.withCString {
let len = Int(strlen($0) + 1)
let dst = strcpy(UnsafeMutablePointer<Int8>.alloc(len), $0)
return (len, dst)
deinit {
// An array of C-style strings (e.g. char**) for easier interop.
class CStringArray {
// Have to keep the owning CString's alive so that the pointers
// in our buffer aren't dealloc'd out from under us.
private let _strings: [CString]
let pointers: [UnsafeMutablePointer<Int8>]
init(_ strings: [String]) {
_strings = { CString($0) }
pointers = { $0.buffer }
// NULL-terminate our string pointer buffer since things like
// exec*() and posix_spawn() require this.
Copy link

helje5 commented Mar 12, 2015

Maybe we could hang on to the closures somehow? Something like:

var mm = [ (UnsafePointer) : UnsafePointer ]()
let a = { cstr in ..., mm[a] = cstr; }

Just an idea, didn't try. Though I guess the pointer is not necessarily 'retained' by the closure, but free'd externally after the closure invocation. Hm, maybe still worth a try ;-)

Copy link

KingOfBrian commented Oct 21, 2015

Hey -- thanks, this was very helpful! I think you could get rid of the len state if you wanted to by changing the deinit to:

    deinit {
        buffer.dealloc(Int(strlen(buffer) + 1))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment