String.withCString, когда строка равна нулю

Проблема, которая будет описана, относится к моему предыдущему вопросу: string .withCString и UnsafeMutablePointer(mutating: cstring), завернутые в функцию, которая была моим первым подходом к обработке нулевых строк (путем помещения withCString в функцию) и к вопросу, который задал Меки: Почему я не могу передать необязательную строку Swift в функцию C, которая разрешает указатели NULL?

Представьте, что есть c-функция, например:

unsigned long randomSign(char *pin, char *tag_signature, char *tag_data, char *xyz);

Я знаю, что функция работает правильно, если я оборачиваю 4 замыкания string.withCString вокруг соответствующей быстрой функции:

// pin, tag_signature, tag_data and xyz are optional Strings so they may be nil which is a problem for my result.
// corresponding swift function:
// randomSign(pin: UnsafeMutablePointer<Int8>!, tag_signature: UnsafeMutablePointer<Int8>!, tag_data: UnsafeMutablePointer<Int8>!, xyz: UnsafeMutablePointer<Int8>!)
let result = pin.withCString { s1 in return
    tag_signature.withCString {s2 in return
        tag_data.withCString {s3 in return
            xyz.withCString { s4 in return 
                randomSign(UnsafeMutablePointer(mutating: s1), UnsafeMutablePointer(mutating: s2), UnsafeMutablePointer(mutating: s3), UnsafeMutablePointer(mutating: s4))
    }}}}

Итак, Мартин R ответил на более простой пример, что нет необходимости оборачивать замыкания вокруг randomSign(arguments) и UnsafeMutablePointer (мутируя: ...), потому что он также может брать строки и преобразовывать их. Но когда я опускаю замыкания и использую их, как описано Мартином Р, это работало при первом запуске на симуляторе сразу после запуска mac, но при последовательных вызовах randomSign-Function возврат скажет мне, что, например, tag_signature или пин-код будут недействительными (но на самом деле они действительны, и я не знаю почему?!).

Это приводит меня к проблеме, что мне нужны замыкания withCString (на данный момент), но я должен обрабатывать nil-Strings, что приведет к сбою приложения, когда оно должно вернуть результат, потому что оно не может оценить randomSign-Function.

Поэтому я попытался подогнать приведенный ниже подход (также предложенный @Martin R) к Swift 3, но я не тренировался, чтобы адаптировать его.

//Swift-2 written by Martin R
protocol CStringConvertible {
    func withCString<Result>(@noescape f: UnsafePointer<Int8> throws -> Result) rethrows -> Result
}

extension String: CStringConvertible { }

extension Optional where Wrapped: CStringConvertible {
    func withOptionalCString<Result>(@noescape f: UnsafePointer<Int8> -> Result) -> Result {
        if let string = self {
            return string.withCString(f)
        } else {
            return f(nil)
        }
    }
}

//Swift 3: ???

Если кто-нибудь может сказать мне, почему моя функция работает только тогда, когда я использую withCString, а не когда я ее отклоняю, я был бы очень благодарен, а также, если кто-нибудь знает, как решить проблему, т.е. правильно перевести код swift-2 в работающий swift -3 код.


person n.eesemann    schedule 19.05.2017    source источник


Ответы (1)


Проблема с

let result = randomSign(UnsafeMutablePointer(mutating: pin),
                    UnsafeMutablePointer(mutating: tag_signature),
                    UnsafeMutablePointer(mutating: tag_data),
                    UnsafeMutablePointer(mutating: xyz))

заключается в том, что временное представление UTF-8, созданное из строк Swift, действительно только во время каждого вызова UnsafeMutablePointer(), но не обязательно остается действительным во время вызова randomSign(). (Поэтому мое последнее предложение в https://stackoverflow.com/a/44027397/1187415 на самом деле было неверным, я обновил эту часть).

Возможная версия оболочки Swift 3 в https://stackoverflow.com/a/39363769/1187415:

extension Optional where Wrapped == String {
    func withOptionalCString<Result>(_ f: (UnsafeMutablePointer<Int8>?) -> Result) -> Result {
        if let string = self {
            return string.withCString { f(UnsafeMutablePointer(mutating: $0)) }
        } else {
            return f(nil)
        }
    }
}

Это обрабатывает как необязательность , так и приводит указатель строки C к изменяемому указателю (как требуется randomSign()). Это можно назвать как

let result = pin.withOptionalCString { s1 in
    tag_signature.withOptionalCString { s2 in
        tag_data.withOptionalCString { s3 in
            xyz.withOptionalCString { s4 in
                randomSign(s1, s2, s3, s4)
            }
        }
    }
}

Примечание. Теоретически проблемы можно избежать, если изменить сигнатуру randomSign() так, чтобы она принимала const char * параметров:

unsigned long randomSign(const char *pin, const char *tag_signature, const char *tag_data, const char *xyz);

который можно было бы просто назвать как

let result = randomSign(pin, tag_signature, tag_data, xyz)

с необязательными или необязательными строками Swift. Однако в настоящее время это не работает, как сообщается в SR-2814. Swift неправильно передает несколько необязательных строки в функцию C.

person Martin R    schedule 19.05.2017
comment
Хорошо, во-первых, я очень ценю все огромные усилия и работу, которые вы вложили в свои ответы, подкрепив их ссылками, примерами и пояснениями. Так что на практике это означает, что только теоретический подход (изменить его на const char*), о котором вы упомянули, не будет работать из-за этой ошибки? Но вышеуказанное расширение будет работать? Расширение помечается красным как: требование того же типа делает общий параметр «обернутым» неуниверсальным:/ - person n.eesemann; 19.05.2017
comment
@n.eesemann: компилируется в моем Xcode 8.3.2 (Swift 3.1). – Да, последний подход должен работать, но в настоящее время он не работает с более чем одной необязательной строкой, как сообщается в этой ошибке. - person Martin R; 19.05.2017
comment
только что обновил Xcode и swift, теперь предложенный вами код расширения работает и на моей машине :). Миллион благодарностей, я бы проголосовал за ваш ответ, но мне не хватает 4 очков репутации! - person n.eesemann; 19.05.2017