WebFront C++23
EN ↗GitHub ↗

Tome II / CHAPITRE 02

Pour comprendre et contribuer

Une application et ses connexions

Une fenêtre peut disparaître pendant qu’un appel attend sa réponse. Voyons quels objets doivent alors rester en vie.

Conventions de notation

Les noms de fonctions, types, commandes, options et fichiers sont composés en chasse fixe, par exemple describe, wf test ou main.cpp. L’italique introduit un terme lors de sa définition. Les blocs de code donnent les extraits à saisir ; le texte qui les précède précise leur emplacement et leurs prérequis.

Ce que nous construisons

Dans un petit exemple, nous commençons généralement par un objet webfront::WebFront. Ce nom nous évite de choisir toutes ses pièces. En poursuivant la lecture des en-têtes, nous rencontrons le type plus général BasicWF<NetProvider, Filesystem, Policy, Frontend>.

Ses paramètres correspondent à des choix assez concrets : obtenir des octets du réseau, trouver les ressources demandées, fixer la politique des buffers et ouvrir la page. Une application ordinaire peut garder les choix par défaut. Un test peut remplacer certaines pièces par des objets dont il contrôle le comportement.

L’implémentation se trouve dans les en-têtes et s’intègre par la cible CMake WebFront::WebFront. C’est le sens de header-only ici. Les dépendances et le travail d’instanciation des templates existent toujours ; les définitions doivent aussi rester compatibles avec une inclusion dans plusieurs unités de traduction.

Qui garde une connexion en vie ?

Supposons qu’un navigateur soit connecté et qu’une opération attende son prochain message. La connexion ne peut pas disparaître simplement parce que le programme a demandé la fermeture de la fenêtre : l’opération en attente s’en sert encore.

BasicWF conserve le serveur, les connexions WebLink, les callbacks enregistrés et le thread serveur. Une WebLink prend en charge les échanges du pont avec un navigateur. Ces objets nous donnent des points précis où chercher les propriétaires.

La difficulté se présente à la fin : nous devons laisser les opérations terminer leurs derniers accès avant de libérer les objets. Nous reviendrons sur cet ordre dans le chapitre consacré à l’arrêt.

Reconnaître le bon navigateur

Une valeur UI permet de désigner le navigateur auquel nous voulons parler. Copier cette valeur est utile, mais ne revient pas à conserver l’application en vie. Nous devons toujours terminer ses utilisations avant la destruction de l’application.

Il existe un autre piège, moins visible. Imaginons qu’une connexion porte le numéro 7, qu’elle se ferme, puis que ce numéro soit attribué à une nouvelle connexion. Une ancienne valeur UI ne doit pas atteindre ce nouveau navigateur.

C’est pourquoi BasicUI conserve une génération en plus de l’application et du numéro de connexion. Le nombre peut être réutilisé ; la génération distingue les deux connexions. Un appel typé visant l’ancienne échoue au lieu de recevoir, par hasard, une réponse d’un autre navigateur.

Un callback peut nous survivre

Enregistrer un callback signifie confier à l’application une fonction qu’elle appellera plus tard. Le mot « plus tard » mérite notre attention. Si la fonction capture une variable locale par référence, cette variable peut avoir disparu lorsque le navigateur provoquera l’appel.

Le même problème se présente lorsqu’un handler conservé capture par référence un paramètre reçu par référence de transfert. Le passage d’argument était valable pendant l’enregistrement ; il ne suffit pas à rendre la capture valable pendant les appels futurs.

Les abonnements posent une question voisine. onUIStarted définit le gestionnaire de l’application. Un composant qui souhaite être averti lui aussi utilise subscribeUIStarted et conserve le UISubscription retourné. Nous pouvons ainsi voir dans son code combien de temps il souhaite participer, sans remplacer le gestionnaire déjà installé.

Vérifier sa compréhension

La copie d’une UI prolonge-t-elle la vie de l’application ?