web-dev-qa-db-fra.com

Utilisation de chaînes C dans Swift ou: Comment convertir UnsafePointer <CChar> en CString

En jouant avec les fonctions de la bibliothèque C standard dans Swift, j'ai rencontré des problèmes lors du passage des chaînes C. Comme exemple simple (juste pour illustrer le problème), la fonction de bibliothèque C standard

char * strdup(const char *s1);

est exposé à Swift as

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

ce qui signifie que la valeur de retour de strdup() ne peut pas être transmise à un autre appel 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

Ma question est: Comment créer un Swift CString à partir d'un UnsafePointer<CChar> , de sorte que la chaîne C renvoyée par une fonction de bibliothèque standard puisse être passée à une autre fonction?

La seule façon que j'ai pu trouver est (en utilisant le code de Comment convertir une chaîne en CString dans la langue Swift? ):

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

Mais je ne trouve pas cela satisfaisant pour deux raisons:

  • C'est trop compliqué pour une tâche simple.
  • (Raison principale:) Les conversions ci-dessus ne fonctionnent que si la chaîne C est une chaîne UTF-8 valide, sinon elle échoue avec une exception d'exécution. Mais une chaîne C est une séquence de caractères arbitraire, délimitée par un caractère NUL.

Remarques/Contexte: Bien sûr, les fonctions de haut niveau utilisant des structures de données de haut niveau comme Swift String ou Objective-C NSString sont préférables, mais il existe des fonctions BSD dans la bibliothèque C standard qui n'ont pas de contrepartie exacte dans les frameworks Foundation.

J'ai rencontré ce problème en essayant de répondre Accès au répertoire temporaire dans Swift . Ici, mkdtemp() est une fonction BSD pour laquelle il n'existe pas de remplacement exact de NSFileManager (pour autant que je sache). mkdtemp() renvoie un UnsafePointer<CChar> qui doit être passé à la fonction NSFileManagerstringWithFileSystemRepresentation qui prend un argument CString.


Mise à jour: Depuis Xcode 6 beta 6, ce problème n'existe plus car le mappage des chaînes C en Swift a été simplifié. Vous pouvez simplement écrire

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

Swift 1.1 (ou peut-être plus tôt) a un pontage de chaîne C encore meilleur:

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

Le type CString a complètement disparu.

17
Nate Cook

La structure String dans Swift a une routine d'initialisation que vous pouvez utiliser comme:

let myString = String(cString: myUnsafePointer)

voir aussi init (cString: UnsafePointer)

0
Alessandro Giusa