Présentation de Swift / C Bridge avec l'histoire du portage d'Echo Server à l'aide de libuv

Je savais que je pourrais travailler avec des bibliothèques de langage C directement à partir de Swift, mais je ne l'avais jamais fait auparavant, c'est donc un tutoriel pour libuv uvbook tcp-echo-server Je voudrais porter) sur Swift pour commencer avec le pont en langage C.

Environnement

Cette fois, j'utiliserai Xcode sur Mac pour porter tcp-echo-server. Le code source est ici https://github.com/yokochie/EchoServer-Sample

La version Xcode sera 7.2 et la version Swift sera 2.1.1.

Installer libuv

libuv est requis pour porter le serveur tcp-echo-server de libuv. L'installation est facile avec Homebrew.

$ brew install --HEAD libuv

Une fois installés, les fichiers requis seront dans / usr / local / {include, lib}.

$ ls /usr/local/include/uv*.h
/usr/local/include/uv-darwin.h		/usr/local/include/uv-threadpool.h	/usr/local/include/uv-version.h
/usr/local/include/uv-errno.h		/usr/local/include/uv-unix.h		/usr/local/include/uv.h
$ ls /usr/local/lib/libuv*
/usr/local/lib/libuv.1.dylib	/usr/local/lib/libuv.a		/usr/local/lib/libuv.dylib

Créer un projet Xcode

Créez un projet d'outil de ligne de commande en vous référant à l'article Création d'un moniteur de paquets avec Swift et Libpcap | Developers.IO.

Pour utiliser libuv, ajoutez d'abord libuv.dylib à partir des paramètres du projet et ajoutez / usr / local / include /, / usr / local / lib au chemin de recherche d'en-tête et au chemin de recherche de la bibliothèque dans le chemin de recherche. LibrarySetting.png SearchPath.png

Autoriser l'appel de libuv

Créez en quelque sorte Bridging-Header.h (facile de créer un nouveau fichier Objective-C et de le supprimer),

#include <uv.h>

Ajouter.

Ensuite, la méthode libuv sera suggérée comme indiqué dans la figure ci-dessous. suggest.png

Introduction à Swift / C Bridge

https://github.com/yokochie/EchoServer-Sample/blob/master/EchoServer/main.swift Osez l'implémentation en langage C (https://github.com/nikhilm/uvbook/blob/master/code/tcp -J'ai posté quelque chose de similaire à (echo-server / main.c). Comme le montre la figure à la fin de la construction de l'environnement, la fonction peut être appelée telle quelle, donc si les types de variables sont correctement mis en correspondance, elle peut être facilement portée. Je pense que ce sera plus facile à comprendre si vous les comparez en lisant cet article.

À propos du type de variable

[Utilisation de Swift avec Cocoa et Objective-C (Swift 2.1): Interagir avec les API C](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/ Il est converti en un type commençant par l'acronyme C, tel que "CChar", "CInt", comme décrit dans doc / uid / TP40014216-CH8-ID17). (Cependant, le type suggéré sur XCode est un type avec un nombre explicite de bits, tel que ʻInt8 ou ʻInt32.)

De plus, pour les pointeurs, Type * devient ʻUnsafeMutablePointer et const Type * devient ʻUnsafePointer <Type>. De plus, «NULL» devient «nil».

Basé sur la conversion de type normal et la conversion de type de pointeur

int * hoge = NULL

Mais

var hoge: UnsafeMutablePointer<CInt> = nil

Sera.

À propos de la gestion des pointeurs

La structure C peut être utilisée telle quelle comme structure Swift, mais si c'est une fonction qui nécessite un pointeur vers une variable Est OK si vous le préfixez avec & (comme si vous aviez préfixé la définition de méthode avec inout). Le code source pour cette heure est main.swift: 66.

uv_ip4_addr("0.0.0.0", CInt(DEFAULT_PORT), &addr)

Cependant, cette technique ne semble pas fonctionner si des lancers se produisent. https://github.com/nikhilm/uvbook/blob/master/code/tcp-echo-server/main.c#L64

uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);

Dans l'état actuel des choses, l'ajout de & et le portage ne correspondaient pas au type et entraînait une erreur de compilation. Par conséquent, [fonction withUnsafePointer](https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_StandardLibrary_Functions/index.html#//apple_ref/swift/func/s:FSs17withUnsterqPointerVFUF_s: FSs17withUnsterqPointerVSFUF0s17withUnsterq Je caste en utilisant le ʻinitializer.

withUnsafePointer(&addr) {
    uv_tcp_bind(&server, UnsafePointer<sockaddr>($0), 0)
}

(UnsafePointer Structure Reference n'a rien qui puisse prendre directement le type de la variable à l'intérieur du pointeur. S'il vous plaît soyez prudente)

De plus, bien que libuv utilise beaucoup les fonctions de rappel, il était possible de spécifier les fonctions déclarées globalement telles quelles. Par exemple, la fonction ʻuv_listenprendvoid (* uv_connection_cb) (uv_stream_t * server, int status)à la fin de l'argument. Https://github.com/yokochie/EchoServer-Sample/blob/master/EchoServer/main.swift#L73 implémenté dans Swift acceptefunc on_new_connect (server: UnsafeMutablePointer <uv_stream_t>, status: CInt)` tel quel Je peux le passer.

Manipulation à l'intérieur du pointeur

En C, vous pouvez utiliser les opérateurs * et -> pour référencer des variables dans des pointeurs, mais dans Swift vous utilisez la propriété memory (https://github.com/yokochie/EchoServer). -Utilisé dans Sample / blob / master / EchoServer / main.swift # L13) Utilisez la méthode ʻUnsafeMutablePointer .alloc (size) pour allouer de la mémoire et la méthode destory` pour la libérer.

Autre

Il semble que la bibliothèque standard Swift n'ait pas de fonction à générer vers la sortie d'erreur standard, donc cette fois, j'émets une déclaration d'erreur vers la sortie standard. Il semble que vous puissiez le faire en ouvrant OutputStream comme décrit dans http://ericasadun.com/2015/06/09/swift-2-0-how-to-print/.

Confirmation de la mise en œuvre

Appuyez sur ⌘R sur Xcode pour exécuter. Puis dans le terminal

$ telnet localhost 7000

Éxécuter. Entrez un caractère tel que «hoge», et si la même chaîne de caractères est affichée, c'est OK.

Exemple de confirmation d'exécution

$ telnet localhost 7000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hoge
hoge

Si vous souhaitez construire à partir de la ligne de commande

$ swiftc main.swift -import-objc-header ../EchoServer-Bridging-Header.h -I/usr/local/include -L/usr/local/lib -luv

Si vous le faites, vous aurez plus de fichiers main dans le même répertoire, vous pourrez donc l'exécuter.

Utiliser la fermeture au lieu de la fonction globale

Outil de ligne de commande réalisé avec Swift2 - Blog des développeurs de Cookpad, comme décrit dans la section "Accrocher le signal", la fermeture Peut être passé en tant que pointeur de fonction. Nous avons préparé une implémentation de https://github.com/yokochie/EchoServer-Sample/blob/use_closure/EchoServer/main.swift au lieu d'une fermeture.

Je ne pense pas que cela changera beaucoup, mais le type de fermeture normal est précédé de @convention (c). (Ci-après, un exemple)

let alloc_buffer: @convention(c) (UnsafeMutablePointer<uv_handle_t>, size_t, UnsafeMutablePointer<uv_buf_t>) -> Void = { (handle, suggested_size, buf) in
    buf.memory.base = UnsafeMutablePointer<CChar>.alloc(suggested_size)
    buf.memory.len = suggested_size
}

À la fin (impression)

Pour la première fois, j'ai touché à la bibliothèque de langage C de Swift, et j'ai été surpris qu'elle soit plus facile à gérer que prévu. En comparant le code source, il existe des différences dans la manière dont les variables et les fonctions sont définies, mais je pense qu'elles sont très similaires.

Je pense que Swift attire l'attention en tant que langage d'implémentation d'application iOS, mais je pense qu'il deviendra plus intéressant lorsque les programmes serveur, etc. seront implémentés dans Swift.

Cet article est devenu une collection d'articles de différentes personnes, mais j'espère que plus de gens essaieront la même chose.

Les références

Recommended Posts

Présentation de Swift / C Bridge avec l'histoire du portage d'Echo Server à l'aide de libuv
Premiers pas avec Swift
Premiers pas avec Language Server Protocol avec LSP4J
Prise en main des programmes Java à l'aide de Visual Studio Code
Premiers pas avec DBUnit
Utiliser la bibliothèque C avec Swift en utilisant les modules Clang (modulemap)
Premiers pas avec Ruby
Premiers pas avec Doma-Transactions
Premiers pas avec le traitement Doma-Annotation
Premiers pas avec Java Collection
Premiers pas avec JSP et servlet
Premiers pas avec les bases de Java
Premiers pas avec Spring Boot
Premiers pas avec les modules Ruby
Premiers pas avec Java_Chapitre 5_Exercices pratiques 5_4
[Google Cloud] Premiers pas avec Docker
Premiers pas avec Docker avec VS Code