Swift vous permet d'appeler normalement les appels de la bibliothèque C existants et les appels système. C'est correct du point de vue de l'utilisation des actifs passés, mais comme Swift est plus strict de type que C, il n'est pas rare d'avoir des difficultés à appeler les fonctions C.
Cette fois, j'ai réécrit le troisième argument start_routine
de pthread_create
de la fonction C à la fermeture Swift. J'espère qu'il sera utile de voir ce qu'il advient du code qui est typé en C dans Swift.
(Parce que je suis un débutant de Swift, faites-le moi savoir s'il y a des erreurs)
pthread_create
crée un nouveau thread et passe le pointeur de fonction de la fonction à exécuter dans le nouveau thread avec le troisième argument start_routine
. Les arguments de la fonction et les valeurs de retour sont void *
, donc si vous souhaitez utiliser différents types d'arguments et de valeurs de retour, vous devez effectuer un transtypage à la fois sur l'appelant et l'appelé.
C'est une idée de type C de faire quoi que ce soit «vide *», mais bien sûr cela ne va pas bien avec Swift.
Cette fois, je voulais utiliser une fonction qui prend ʻintet
char ** comme arguments et renvoie ʻint
(en bref, c'est une fonction main
) de pthread_create
, donc j'ai utilisé une fonction wrapper pour cela. Je l'ai écrit en C.
void *pthread_main_routine(void *_arg)
{
void **arg = (void **)_arg;
int argc = (int)arg[0];
char **argv = (char**)arg[1];
void *retval = (void *)(long)orig_main(argc, argv);
return retval;
}
Du côté Swift, cela s'appelait comme suit.
var thread_arg = [
UnsafeMutablePointer<Void>(bitPattern: args.pointers.count-1),
UnsafeMutablePointer<Void>(args.pointers)
]
pthread_create(&thread, nil, pthread_main_routine, &thread_arg)
Cela fonctionnait également dans le code ci-dessus, mais pour diverses raisons, j'ai remplacé la fonction C par une fermeture Swift.
typealias VoidPtr = UnsafeMutablePointer<Void>
typealias VoidPtrArray = [UnsafeMutablePointer<Void>]
typealias CharPtrArray = [UnsafeMutablePointer<CChar>]
let thread_arg = [
unsafeBitCast(args.pointers.count-1, VoidPtr.self),
unsafeBitCast(args.pointers, VoidPtr.self),
]
pthread_create(&thread, nil, {_arg in
let arg = unsafeBitCast(_arg, VoidPtrArray.self)
let argc = Int32(unsafeBitCast(arg[0], Int.self))
let argv = unsafeBitCast(arg[1], CharPtrArray.self)
let retval = unsafeBitCast(Int(orig_main(argc, argv)), VoidPtr.self)
return retval
}, unsafeBitCast(thread_arg, VoidPtr.self))
J'ai réussi à faire quelque chose qui fonctionne avec beaucoup de ʻunsafeBitCast`, mais cela s'est avéré assez fastidieux et difficile à lire. Le code plus facile à écrire en C doit être écrit en C.
Soit dit en passant, veuillez noter que le contenu à transmettre est légèrement modifié selon que le tableau Swift est converti en «void *» ou «char **» en «void *» dans les deux codes.
La gestion des fonctions C dans Swift2 n'est pas bonne, et seul le nom de la fonction globale lui-même ou le littéral de fermeture peut être placé dans le troisième argument de ce pthread_create
. Si vous affectez une fonction à une variable et donnez cette variable comme argument, vous obtiendrez une erreur de compilation comme suit.
A C function pointer can only be formed from a reference to a 'func' or a literal closure
De plus, n'écrivez pas de code qui dépend de l'environnement, même s'il s'agit d'un littéral de fermeture. Par exemple, référencer une variable d'instance à partir de la fermeture entraînera une erreur de compilation comme indiqué ci-dessous.
A C function pointer cannot be formed from a closure that captures context
Je pense que ce dernier ne peut pas être aidé, mais qu'en est-il du premier? Je me sens comme ça. J'espère que Swift3 sera plus facile à manipuler ici.
Recommended Posts