Mutable C String в Swift - PullRequest
       122

Mutable C String в Swift

0 голосов
/ 04 мая 2020

Я пишу код Swift, который взаимодействует с библиотекой C. Библиотека предоставляет неполную структуру, которую я обертываю в класс Swift. Функция инициализатора для этой структуры принимает аргумент char* или UnsafeMutablePointer<UInt8> в Swift. Библиотека не предоставляет способ назначить другой указатель позже.

Я пытаюсь выяснить, как получить UnsafeMutablePointer<UInt8> для строки Swift, с двумя основными требованиями. Во-первых, строка должна быть изменяемой без изменения указателя. Во-вторых, время жизни указателя (и строки) должно быть таким же, как и у объекта-оболочки.

Возможно ли это, и если да, то как?

1 Ответ

2 голосов
/ 04 мая 2020

Указатель на строковое представление C строки Swift получается с помощью

s.withCString { cStringPtr in
    callCFunction(cStringPtr)
}

, но есть две проблемы: во-первых, хранилище, на которое указывает указатель, не является изменяемым. Если функция C объявлена ​​как принимающая char *, но фактически не изменяющая строку (т.е. если она должна быть объявлена ​​как принимающая аргумент const char *), то вы можете сделать указатель изменяемым с

s.withCString { cStringPtr in
    let mutableCStringPtr = UnsafeMutablePointer(mutating: cStringPtr)
    callCFunction(mutableCStringPtr)
}

Но этот указатель по-прежнему ссылается на то же (неизменяемое) хранилище, то есть вызывает неопределенное поведение, если функция C изменяет строку C.

Вторая проблема При таком подходе указатель действителен только для выполнения замыкания. Вы не можете взять указатель и сохранить его для дальнейшего использования. Это было бы неопределенным поведением:

let mutablePointer = s.withCString { cStringPtr in
    UnsafeMutablePointer(mutating: cStringPtr)
}

// Use `mutablePointer` later.

Для более длительного времени жизни (т.е. времени жизни экземпляра оболочки) необходимо выделить память и скопировать строку C. Например, это можно сделать с помощью strdup():

class Wrapper {
    var cStringPtr: UnsafeMutablePointer<CChar>

    init(s: String) {
        guard let cStringPtr = strdup(s) else {
            // Handle "no memory" error ...
        }
        self.cStringPtr = cStringPtr
    }

    deinit {
        free(cStringPtr)
    }
}

(Если вам интересно, почему строку Swift можно напрямую передать в strdup(), см. Значение строки в UnsafePointer Поведение параметра функции .)

...