L'idée de base, mais vraiment de base, consiste à clamer à qui veut bien l'entendre que JSON est un format idéal pour passer des données à un système de Vue, comprendre View, dans un MVC. Ce format a tout ce qu'il faut: simple, compact, expressif et connu de tous, ou presque.
Le constat de base, mais vraiment de base (oui vous savez), c'est qu'il y a très peu de systèmes de template pour C++ qui ont un penchant web, c'est à dire principalement orientés génération de HTML. Je ne sais pas pourquoi, peut être parce que tout le monde s'en fout ? ;) Toujours est-il que lorsque je me suis mis en recherche d'un tel système de template, pour des besoins perso, je n'ai rien trouvé d'autre que Clearsilver et google-ctemplate. Il me fallait un truc standalone et léger. J'aurai pu chercher un peu plus, mais je me suis arrêté là, pas vraiment convaincu.
C'est là que je me suis souvenu de l'idée de base: JSON. En fait ça tombait bien puisque j'utilisais déjà le moteur javascript V8 dans mon projet. Cet engine, initialement conçu par google pour le navigateur Chrome, est open source et son API est en C++. L'idée qui a commencé à émerger est la suivante:
- On balance du "JSON" au système de template
- La logique de présentation est controllée par javascript
- Le système crache du text en retour (principalement du HTML)
Sympa. Reste plus qu'à implémenter ça.
Le JSON, expédié avec une technique à base de Boost.Variant récursif que j'ai présenté dans ce billet Simple modélisation de JSON en C++.
Le javascript qui controle la logique de présentation est embarqué dans les templates sous une forme proche d'un mix de Django et de la syntaxe alternative de php.
{% for (i in posts) : %}
<div class="post">
<h2>{%= posts[i].title %}</h2>
...
</div>
{% end %}
Le principe c'est que tout ce qui est entre {% ... %} est grosso modo du javascript qui sera inchangé avant de le donner au moteur js. Une phase de parsing transforme le fichier de template (ou n'importe quel text) en une forme intermédiaire qui sera consumée par V8. Pour l'exemple ci-dessus, cela donne quelque chose proche de ceci:
for (i in posts) {__pr(' <div class="post">\n <h2>');
__p(posts[i].title);__pr('</h2>\n ...\n </div>\n');};
Le parser est écrit avec la librairie Boost.Spirit Classic. __p() pour "print" et __pr() pour "print raw" sont des fonctions javascript dont l'implémentation est en C++ et qui permettent de controller la sortie textuelle finale, par exemple en appliquant des filtres d'échappement automatique pour du HTML. Ces filtres sont inspirés des modifiers de google-ctemplate. Du genre {%:h:s= comment %} pour un filtre h qui échappe du HTML et un autre s qui permet de wrapper des snippets entre des éléments <pre>.
Ce qui est sympathique c'est qu'il est possible de transformer des sources javascript textuelles en un byte code propre à V8. C'est dans l'API. On peut donc mettre en cache des templates pre compilées, un peu comme le fait APC pour php, mais ça dépote encore plus! J'avais effectué quelques benchmarks qui laissaient penser que ce moteur de template à base de V8 était plus véloce que php+APC. Il faudrait que je mesure ça plus sérieusement.
A vrai dire ce projet de système de template a été codé à l'arrache courant novembre 2009. Un simple proof of concept à l'origine. Les dernières librairies de Boost notamment Spirit 2 (ça tue!), Phoenix 2 et Fusion 2 (OK, beaucoup de 2) m'ont donné envie de réécrire un tel système. J'héberge le truc sur github. Je lui ai donné le joli nom de r8t, réunion de rat et V8. Pour l'instant, c'est succinct. Pourquoi rat? Parce que ;)