Tome II / CHAPITRE 03
Pour comprendre et contribuer
Ce qui se passe lors d’un appel
Suivons une demande depuis le bouton de la page jusqu’à la fonction C++, puis revenons avec sa réponse.
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.
- Page JavaScript
- Codec et WebSocket
- WebLink et routage
- Callback C++
La réponse ou l’erreur revient vers la demande en attente. Les deux sens ne proposent pas le même contrat de résultat.
Charger la page, puis lui parler
Pour afficher son premier bouton, le navigateur doit recevoir un document et les ressources qui le composent. HTTP convient à ce travail : le navigateur demande une ressource, le serveur lui répond.
Une fois la page ouverte, nous voulons aussi échanger avec le programme C++. Recharger le document à chaque clic serait peu commode. La connexion WebSocket conserve un canal sur lequel la page et le programme peuvent envoyer leurs messages.
Du côté C++, les coroutines attendent les opérations d’acceptation, de réception et d’envoi du fournisseur réseau. TCPStdnet les relie à stdnet. Le choix du moteur d’affichage reste ailleurs : une fenêtre WKWebView et un navigateur système parlent au même serveur.
Le clic devient un message
Reprenons le bouton qui demande la prochaine génération. La page a d’abord attendu webFront.ready, afin que la connexion ait été acquittée. Elle obtient ensuite la fonction nommée avec webFront.cppFunction(name) et l’appelle avec ses arguments.
Le navigateur ne peut pas entrer directement dans une fonction C++. Un codec transforme donc le nom et les arguments en octets du protocole. À l’arrivée, le code C++ décode le message et retrouve le callback enregistré.
Il reste à revenir vers l’appelant. Un résultat résout sa promesse ; une erreur la rejette. Même une fonction C++ qui renvoie void peut échouer. Son appel JavaScript produit donc lui aussi une Promise, que la page doit attendre ou dont elle doit traiter le rejet.
Quand le C++ appelle la page
Dans l’autre sens, tous les appels n’ont pas besoin d’un résultat. Pour demander à une page d’afficher un nouvel état, la forme ui.jsFunction(name) peut suffire : elle envoie l’appel et ignore sa valeur de retour.
Si le programme a besoin de cette valeur, il utilise ui.jsFunction<R>(name) et reçoit un std::future<R>. La forme ui.jsFunctionSender<R> sert à composer l’attente avec les tâches et leur annulation locale. Ces choix expriment ce que l’appelant compte faire après l’envoi.
Une demande en attente doit rester reconnaissable jusqu’à sa réponse, son échec ou son arrêt. Si la réponse arrive après l’arrêt, elle ne doit pas terminer une seconde fois la même demande. Nous retrouverons cette petite course dans les tests de senders.
Se mettre d’accord sur les octets
Le code C++ de Messages.hpp et le JavaScript de js/core/ doivent donner le même sens aux octets échangés. Les scalaires, les chaînes, les tuples, les tableaux numériques et les bitsets ont chacun une représentation définie. Ajouter un objet JavaScript à un exemple ne suffit pas à créer une représentation pour cet objet.
Prenons un bitset de dix bits : deux octets suffisent, mais six bits du dernier octet ne portent aucune donnée utile. Le décodeur vérifie qu’ils sont nuls. Cette vérification évite que deux messages différents soient acceptés pour une représentation qui devrait être unique. Les tailles et les types de tableaux demandent la même attention.
Lorsque nous modifierons un message, il faudra donc lire les deux codecs et leurs bornes. Un test de décodage C++ peut établir une propriété précise ; un test dans un vrai navigateur vérifie que l’autre extrémité est toujours d’accord.
Vérifier sa compréhension