Trabajar con cadenas C en Swift, o: Cómo convertir UnsafePointer<CChar> a CString
(1)
Swift 1.1 (o quizás antes) tiene un puente de cadena C aún mejor:
let haystack = "This is a simple string"
let needle = "simple"
let result = String.fromCString(strstr(haystack, needle))
El tipo CString
se ha ido por completo.
Mientras jugaba con las funciones de la Biblioteca C estándar en Swift, encontré problemas al pasar las cadenas C alrededor. Como un ejemplo simple (solo para demostrar el problema), la función de la biblioteca de C estándar
char * strdup(const char *s1);
está expuesto a Swift como
func strdup(_: CString) -> UnsafePointer<CChar>
lo que significa que el valor de retorno de strdup()
no se puede pasar a otra strdup()
a 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
Mi pregunta es: ¿Cómo crear un Swift CString
desde un UnsafePointer<CChar>
, para que la cadena C devuelta por una función de biblioteca estándar pueda pasarse a otra función?
La única forma que pude encontrar es (usando el código de ¿Cómo convertir una cadena a una cadena CS en el lenguaje Swift? ):
let s2a = String.fromCString(s2).bridgeToObjectiveC().UTF8String
let s3 = strdup(s2a)
Pero no encuentro esto satisfactorio por dos razones:
- Es demasiado complicado para una tarea sencilla.
- (Razón principal :) Las conversiones anteriores solo funcionan si la cadena C es una cadena UTF-8 válida; de lo contrario, falla con una excepción de tiempo de ejecución. Pero una cadena C es una secuencia arbitraria de caracteres, delimitada por un carácter NUL.
Observaciones / antecedentes: por supuesto, son preferibles las funciones de alto nivel que utilizan estructuras de datos de alto nivel como Swift String
o Objective-C NSString
. Pero hay funciones BSD en la biblioteca estándar de C que no tienen una contraparte exacta en los marcos de trabajo de Foundation.
Encontré este problema al intentar responder Accediendo al directorio temporal en Swift . Aquí, mkdtemp()
es una función BSD para la cual no existe un reemplazo exacto de NSFileManager
(que yo sepa). mkdtemp()
devuelve un UnsafePointer<CChar>
que se debe pasar a la función stringWithFileSystemRepresentation
que toma un argumento CString
.
Actualización: a partir de Xcode 6 beta 6, este problema ya no existe porque se ha simplificado el mapeo de C-Strings en Swift. Solo puedes escribir
let s1 = "abc" // String
let s2 = strdup(s1) // UnsafeMutablePointer<Int8>
let s3 = strdup(s2) // UnsafeMutablePointer<Int8>
let s4 = String.fromCString(s3) // String