Работа со строками C в Swift или: Как преобразовать UnsafePointer <CChar> в CString

Во время игры со стандартными функциями библиотеки C в Swift я столкнулся с проблемами при прохождении C строк вокруг. В качестве простого примера (просто для демонстрации проблемы) функция Standard C Library

char * strdup(const char *s1);

подвергается воздействию Swift как

func strdup(_: CString) -> UnsafePointer<CChar>

что означает, что возвращаемое значение strdup() не может быть передано другому вызову strdup():

let s1 : CString = "abc"
let s2 = strdup(s1) // OK, s2 is a UnsafePointer<CChar>
let s3 = strdup(s2) // error: could not find an overload for '__conversion' that accepts the supplied arguments

Мой вопрос: Как создать Swift CString с UnsafePointer<CChar>, так что строка C, возвращаемая одной стандартной библиотечной функцией, может быть передана другой функции?

Единственный способ, которым я мог найти (с помощью кода из Как преобразовать String в CString на языке Swift?):

let s2a = String.fromCString(s2).bridgeToObjectiveC().UTF8String
let s3 = strdup(s2a)

Но я не считаю это удовлетворяющим по двум причинам:

  • Это слишком сложно для простой задачи.
  • (Основная причина:) Вышеперечисленные преобразования работают только в том случае, если строка C является допустимым UTF-8 string, в противном случае он завершится неудачей с использованием исключения времени выполнения. Но строка C является произвольной последовательность символов, ограниченная символом NUL.

Примечания/Предпосылки: Конечно, предпочтительны высокоуровневые функции с использованием высокоуровневых структур данных, таких как Swift String или Objective-C NSString. Но есть функции BSD в Standard C Library, которые не имеют точного аналога в рамках Foundation.

Я столкнулся с этой проблемой, пытаясь ответить Доступ к временному каталогу в Swift. Здесь mkdtemp() - это функция BSD, для которой не существует точной замены NSFileManager (насколько я знаю).  mkdtemp() возвращает a UnsafePointer<CChar>, который должен быть передан в NSFileManager функция stringWithFileSystemRepresentation, которая принимает значение CString аргумент.


Обновление: с Xcode 6 beta 6 эта проблема больше не существует, потому что сопоставление C-строк в Swift было упрощено. Вы можете просто написать

let s1 = "abc"      // String
let s2 = strdup(s1) // UnsafeMutablePointer<Int8>
let s3 = strdup(s2) // UnsafeMutablePointer<Int8>
let s4 = String.fromCString(s3) // String

Ответ 1

Swift 1.1 (или, возможно, более ранний) имеет еще лучшее связывание строки C:

let haystack = "This is a simple string"
let needle = "simple"
let result = String.fromCString(strstr(haystack, needle))

Тип CString полностью отсутствует.