I. Introduction▲
À l'image de Javadoc, l'outil d'autodocumentation pour Java, Doxygen permet de créer des documentations techniques pour notamment le C et le C++, mais couvre également d'autres langages, y compris Java !
Ce qui sera étudié dans ce tutoriel, c'est l'utilisation basique de Doxygen avec un petit détour vers des fonctions avancées pour générer des documentations de références par exemple celles de GTK+.
II. Les balises standards▲
Ce chapitre vous présente succinctement les balises les plus utilisées avec Doxygen. Pour la liste complète, rendez-vous sur le site officiel: Special Commands.
II-A. Les blocs de documentation▲
Diverses combinaisons sont possibles pour créer des blocs de documentation, dans le style C ou C++ pour notre cas !
/**
* ... Documentation ...
*/
/*!
* ... Documentation ...
*/
///
/// ... Documentation ...
///
//!
//! ... Documentation ...
//!
II-B. \file▲
Permet de créer un bloc de documentation pour un fichier source ou d'en-tête.
\file [<name>]
\file main.c
II-C. \brief▲
Permet de créer une courte description dans un bloc de documentation. La description peut se faire sur une seule ligne ou plusieurs.
\brief {brief description}
\brief Courte description.
\brief Courte description.
Suite de la courte description.
II-D. \author▲
Permet d'ajouter un nom d'auteur (ex. : l'auteur d'un fichier ou d'une fonction). Plusieurs balises peuvent être présentes, lors de la génération un seul paragraphe Auteur sera créé, mais les listes d'auteurs seront séparées d'une ligne vide. Une seule balise peut accueillir plusieurs auteurs.
\author { list of authors }
\author Franck.H
II-E. \version▲
Permet l'ajout du numéro de version (ex. : dans le bloc de documentation d'un fichier).
\version { version number }
\version 0.1
II-F. \date▲
Permet l'ajout d'une date. Plusieurs balises peuvent être présentes, lors de la génération un seul paragraphe Date sera créé, mais chaque date sera séparée par une ligne vide. Une seule balise peut accueillir plusieurs dates.
\date { date description }
\date 10 septembre 2007
II-G. \struct▲
Permet la création d'un bloc de documentation pour une structure. Cette balise peut prendre jusqu'à trois arguments qui sont dans l'ordre :
- Nom de la structure ;
- Nom du fichier d'en-tête (ex. : le fichier où elle est définie) ;
- Nom optionnel pour masquer le nom affiché par le second argument.
Si le second argument est fourni, un lien HTML vers le code source du fichier d'en-tête spécifié sera créé. Le dernier argument permet quant à lui de simplement changer éventuellement le nom de ce lien qui par défaut est le nom du fichier fournit en second argument.
\struct <name> [<header-file>] [<header-name>]
\struct Str_t
\struct Str_t str.h "Définition"
II-H. \enum▲
Permet la création d'un bloc de documentation pour une énumération de constantes.
\enum <name>
\enum Str_err_e
II-I. \union▲
Permet la création d'un bloc de documentation pour une union. L'utilisation est la même que pour une structure (voir le chapitre II-G\struct).
\union <name> [<header-file>] [<header-name>]
II-J. \fn▲
Permet la création d'un bloc de documentation pour une fonction ou méthode.
\fn (function declaration)
\fn int main (void)
Il est possible d'omettre cette balise lorsque le bloc de documentation commence juste au-dessus de la déclaration/définition de la fonction/méthode. Doxygen ajoutera de lui-même la signature de la fonction ou de la méthode. On peut donc en conclure que nous pouvons documenter une fonction à peu près n'importe où dans le code, mais dans ce cas, il faut ajouter la balise !
II-K. \def▲
Permet la création d'un bloc de documentation pour une macro ou constante symbolique.
\def <name>
\def MAX(x,y)
II-L. \param▲
Permet d'ajouter un paramètre dans un bloc de documentation d'une fonction ou d'une macro. Le premier paramètre est le nom de l'argument à documenter et le second le bloc de description le concernant.
\param <parameter-name> { parameter description }
\param self Pointeur sur un objet de type Str_t.
Cette balise permet aussi d'ajouter en option un tag pour montrer qu'il s'agit d'un argument entrant, sortant ou les deux en précisant au choix : [in], [out] ou [in, out] de cette manière :
\param[in] self Pointeur sur un objet de type Str_t.
La sortie serait alors :
Paramètres:
[in] self Pointeur sur un objet de type Str_t.
II-M. \return▲
Permet de décrire le retour d'une fonction ou d'une méthode.
\return { description of the return value }
\return Instance de l'objet, NULL en cas d'erreur.
II-N. \bug▲
Permet de commencer un paragraphe décrivant un bug (ou plusieurs). L'utilisation est identique à la balise \author (voir le chapitre).
\bug { bug description }
\bug Problème d'affichage du texte en sortie
II-O. \deprecated▲
Permet d'ajouter un paragraphe précisant que la fonction, marco, etc. est dépréciée, qu'il ne faut donc plus l'utiliser.
\deprecated { description }
\deprecated Fonction dépréciée, ne plus utiliser !
II-P. \class▲
Permet de créer un bloc de documentation d'une classe (C++). L'utilisation est identique à la balise \struct (voir le chapitre).
\class <name> [<header-file>] [<header-name>]
\class Str
II-Q. \namespace▲
Permet de créer un bloc de documentation pour un espace de nom (C++).
\namespace <name>
\namespace std
III. Mise en place de la documentation▲
III-A. Informations d'en-tête▲
Nous allons voir ici une manière de mettre un bloc d'informations d'en-tête d'un fichier avec les numéros de versions, auteurs, nom de fichiers, etc.
/**
*
\file
main.c
*
\brief
Programme de tests.
*
\author
Franck.H
*
\version
0.1
*
\date
11 septembre 2007
*
* Programme de test pour l'objet de gestion des chaines de caractères Str_t.
*
*/
L'ordre des balises n'a que très peu d'importance, mais cela a un impact sur l'ordre de génération du paragraphe et donc de l'affichage. Ce qu'on peut remarquer de plus et qui n'a pas été abordé jusque-là, c'est qu'on peut ajouter des commentaires en dehors de la balise . Ceci sera en effet considéré par Doxygen comme une description détaillée et se trouvera alors dans un paragraphe intitulé Description détaillée.
On peut également voir que dans le champ date, la date peut prendre la forme que l'on souhaite. La balise sert surtout pour créer le paragraphe avec le bon titre.
III-B. Documentation d'une fonction/méthode▲
Que ce soit pour une fonction ou une méthode de classes (C++), ce bloc ne change pas. Il faut noter qu'il doit y avoir une balise par argument. Tous les paramètres seront alors dans un même paragraphe Paramètres !
/**
*
\fn
static Str_t * str_new (const char * sz)
*
\brief
Fonction de création d'une nouvelle instance d'un objet Str_t.
*
*
\param
sz Chaine à stocker dans l'objet Str_t, ne peut être NULL.
*
\return
Instance nouvellement allouée d'un objet de type Str_t ou NULL.
*/
Remarquez que pour la balise , il faut fournir la déclaration complète de la fonction ou de la méthode !
Il ne faut surtout pas oublier de bien fournir la balise, car une vérification syntaxique de celle-ci sera faite par Doxygen et au moindre problème, il ne générera pas votre documentation !
III-C. Documentation d'une structure/union▲
La documentation d'une structure et d'une union se fait de la même manière, nous allons donc voir ici que le cas d'une structure, ce qui sera retranscrit dans les exemples complets plus bas.
/**
*
\struct
Str_t
*
\brief
Objet chaine de caractères.
*
* Str_t est un petit objet de gestion de chaines de caractères.
* La chaine se termine obligatoirement par un zéro de fin et l'objet
* connait la taille de chaine contient !
*/
Jusque-là rien de bien difficile, on peut simplement remarquer que nous n'avons ici, pas renseigné les champs optionnels de la balise. Une particularité qui n'a pas été abordée est que nous pouvons commenter les différents champs d'une structure, d'une union et même des variables membres d'une classe.
Voici comment procéder :
typedef
enum
{
STR_NO_ERR, /*!
< Pas d'erreur.
*/
STR_EMPTY_ERR, /*!
< Erreur: Objet vide ou non initialisé.
*/
NB_STR_ERR /*!
< Nombre total de constantes d'erreur.
*/
}
Str_err_e;
L'opérateur qu'il faut essentiellement retenir est « < » qui permet alors de documenter un membre, ici d'une structure, ce qui produit une génération de ce genre :
Énumération:
STR_NO_ERR Pas d'erreur.
STR_EMPTY_ERR Erreur: Objet vide ou non initialisé.
NB_STR_ERR Nombre total de constantes d'erreur.
III-D. Exemple complet en C▲
Voici un exemple de génération de la documentation de cet exemple : Exemple
/**
*
\file
main.c
*
\brief
Programme de tests.
*
\author
Franck.H
*
\version
0.1
*
\date
6 septembre 2007
*
* Programme de test pour l'objet de gestion des chaines de caractères Str_t.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/**
*
\struct
Str_t
*
\brief
Objet chaine de caractères.
*
* Str_t est un petit objet de gestion de chaines de caractères.
* La chaine se termine obligatoirement par un zéro de fin et l'objet
* connait la taille de chaine contient !
*/
typedef
struct
{
char
*
sz; /*!
< Chaine avec caractère null de fin de chaine.
*/
size_t len; /*!
< Taille de la chaine sz sans compter le zéro de fin.
*/
}
Str_t;
/**
*
\enum
Str_err_e
*
\brief
Constantes d'erreurs.
*
* Str_err_e est une série de constantes prédéfinie pour diverses futures
* fonctions de l'objet Str_t.
*/
typedef
enum
{
STR_NO_ERR, /*!
< Pas d'erreur.
*/
STR_EMPTY_ERR, /*!
< Erreur: Objet vide ou non initialisé.
*/
NB_STR_ERR /*!
< Nombre total de constantes d'erreur.
*/
}
Str_err_e;
/**
*
\fn
static Str_err_e str_destroy (Str_t ** self)
*
\brief
Fonction de destruction de l'objet Str_t.
*
*
\param
self Adresse de ll'objet Str_t à détruire.
*
\return
STR_NO_ERR si aucune erreur, STR_EMPTY_ERR sinon.
*/
static
Str_err_e str_destroy (
Str_t **
self)
{
Str_err_e err =
STR_EMPTY_ERR;
if
(
self !=
NULL
&&
*
self !=
NULL
)
{
free (*
self);
*
self =
NULL
;
err =
STR_NO_ERR;
}
return
err;
}
/**
*
\fn
static Str_t * str_new (const char * sz)
*
\brief
Fonction de création d'une nouvelle instance d'un objet Str_t.
*
*
\param
sz Chaine à stocker dans l'objet Str_t, ne peut être NULL.
*
\return
Instance nouvelle allouée d'un objet de type Str_t ou NULL.
*/
static
Str_t *
str_new (
const
char
*
sz)
{
Str_t *
self =
NULL
;
if
(
sz !=
NULL
&&
strlen (
sz) >
0
)
{
self =
malloc (
sizeof
(*
self));
if
(
self !=
NULL
)
{
self->
len =
strlen (
sz);
self->
sz =
malloc (
self->
len +
1
);
if
(
self->
sz !=
NULL
)
{
strcpy (
self->
sz, sz);
}
else
{
str_destroy (&
self);
}
}
}
return
self;
}
/**
*
\fn
int main (void)
*
\brief
Entrée du programme.
*
*
\return
EXIT_SUCCESS - Arrêt normal du programme.
*/
int
main (
void
)
{
Str_err_e err;
Str_t *
my_str =
str_new (
"
Ma chaine de caracteres !
"
);
if
(
my_str !=
NULL
)
{
printf (
"
%s
\n
"
, my_str->
sz);
printf (
"
Taille de la chaine : %d
\n
"
, my_str->
len);
err =
str_destroy (&
my_str);
if
(!
err)
{
printf (
"
L'objet a ete libere correctement !
\n
"
);
}
}
return
EXIT_SUCCESS;
}
III-E. Exemple complet en C++▲
Voici un exemple de génération de la documentation de cet exemple : Exemple
#ifndef CPLAYER_H_
#define CPLAYER_H_
/*!
*
\file
CPlayer.h
*
\brief
Lecteur de musique de base
*
\author
hiko-seijuro
*
\version
0.1
*/
#include
<string>
#include
<list>
/*!
\namespace
player
*
* espace de nommage regroupant les outils composants
* un lecteur audio
*/
namespace
player
{
/*!
\class
CPlayer
*
\brief
classe representant le lecteur
*
* La classe gere la lecture d'une liste de morceaux
*/
class
CPlayer
{
private
:
std::
list<
string>
m_listSongs; /*!
< Liste des morceaux
*/
std::
list<
string>
::
iterator m_currentSong; /*!
< Morceau courant
*/
public
:
/*!
*
\brief
Constructeur
*
* Constructeur de la classe CPlayer
*
*
\param
listSongs : liste initiale des morceaux
*/
CPlayer(std::
list<
string>
listSongs);
/*!
*
\brief
Destructeur
*
* Destructeur de la classe CPlayer
*/
virtual
~
CPlayer();
public
:
/*!
*
\brief
Ajout d'un morceau
*
* Methode qui permet d'ajouter un morceau a liste de
* lecture
*
*
\param
strSong : le morceau a ajouter
*
\return
true si morceau deja present dans la liste,
* false sinon
*/
bool
add(std::
string strSong);
/*!
*
\brief
Morceau suivant
*
* Passage au morceau suivant
*/
void
next();
/*!
*
\brief
Morceau precedent
*
* Passage au morceau precedent
*/
void
previous();
/*!
*
\brief
Lecture
*
* Lance la lecture de la liste
*/
void
play();
/*!
*
\brief
Arret
*
* Arrete la lecture
*/
void
stop();
}
;
}
;
#endif
IV. Avant d'aller plus loin…▲
Si vous n'avez pas encore Doxygen d'installé, il va falloir franchir le pas maintenant. Vous pouvez trouver différentes archives et installeurs pour divers systèmes sur cette page : http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc.
Si vous avez par exemple une distribution comme Debian, vous pouvez passer par les dépôts, mais il vous faut (par rapport à l'installeur pour la version Windows qui inclut le tout) alors installer plusieurs programmes :
sudo aptitude install graphviz doxygen doxygen-gui
Dont graphviz permet la génération de graphiques (entre autres, des graphiques de dépendances… oui oui, il fait également ça ce merveilleux programme !) et doxygen-gui est l'interface graphique (doxywizard) !
V. Configuration de Doxygen avec Doxywizard▲
Allons-y… lancez Doxygen ! Sous Linux par la console vous devez appeler le programme nommé doxywizard :
franhec@lucie:~$ doxywizard
Vous devriez vous retrouver devant une interface semblable à celle-ci :
Nous allons maintenant voir comment configurer un projet. Pour commencer, nous passons par l'assistant donc le bouton Wizard…
V-A. Onglet « Project »▲
- Le nom de votre projet.
- La version du projet.
- Le répertoire du code source dont il faut générer la documentation.
- Si cette case est cochée, Doxygen ira fouiller également dans les sous-répertoires à la recherche de codes sources.
- Emplacement où il faut générer la documentation. Un répertoire html est créé par défaut par Doxygen à l'emplacement spécifié où la documentation y sera placée.
V-B. Onglet « Mode »▲
Dans ce second onglet d'options, dans la première série nous pouvons déterminer le détail du contenu (plus ou moins exactement). Par défaut l'option est sur Documented entities only, ceci permettra simplement la génération de la documentation des parties documentées, les autres ne seront pas référencées. La seconde option permet justement de faire référencer la totalité du code, donc les parties private, static, etc.
En effet, même si vous créez de la documentation pour une fonction privée par exemple, celle-ci ne sera pas ajoutée dans la génération si vous utilisez la première option, mais la seconde fait tout générer, ce que vous ne voulez peut-être pas, nous verrons la façon de régler cette partie avec précision dans le prochain chapitre !
La case à cocher « Include cross-referenced source code in the output » permet l'inclusion du code source complet dans la documentation tout en ajoutant des liens hypertextes pour chaque partie documentée entre la documentation et le code !
La seconde série d'options permet de définir la façon dont le code sera colorisé et présenté, donc par rapport à votre langage !
V-C. Onglet « Output »▲
Ce troisième onglet permet le choix du type de sortie de la documentation. Plusieurs formats sont supportés: HTML, LaTeX, Man pages(pages de manuels, Linux), Rich Text Format(fichier texte au format RTF) et XML. Nous nous intéresserons surtout au format HTML dans ce tutoriel !
Le format HTML permet plusieurs types de présentations, les voici dans l'ordre :
- Page sans zone de navigation, un peu à la manière de ce tutoriel si on veut ;
- Page HTML avec une zone de navigation en arbre (vue hiérarchique) comme vous avez pu en voir dans les exemples de génération précédents ;
- Documentation au forma HTML compilé. C'est en fait le format des fichiers d'aide de Windows ;
- Cette option supplémentaire permet d'ajouter une zone de recherche, pratique pour de très grosses documentations, mais, pour que ce module fonctionne, il faut que PHP soit installé sur le serveur qui héberge, par exemple pour une version en ligne.
V-D. Onglet « Diagrams »▲
Comme il a déjà été mentionné, Doxygen permet également de créer des graphiques, notamment des graphiques de dépendances entre les différents fichiers donc les inclusions. C'est dans cet onglet que nous pouvons choisir les différents graphiques que nous voulons voir s'afficher dans la documentation !
Voici dans l'ordre, les graphiques proposés par Doxygen en utilisant le générateur Graphviz :
- Créer un graphique montrant les relations directes et indirectes de chaque classe documentée. Cette option désactive automatiquement la génération de diagrammes intégrée à Doxygen (l'option nommée « Use built-in classes diagram generator ») ;
- Créer un graphique pour les classes (et structures en ce qui concerne le C) montrant les relations avec les classes de base ainsi que les relations avec d'autres structures/classes (ex. : une structure A qui possède une variable membre du type d'une structure B) ;
- Créer un graphique montrant les dépendances entre les différents fichiers documentés. Cette option ne fonctionne qu'avec la génération au format HTML et RTF !
- Créer un graphique pour chaque fichier d'en-tête documenté montrant les fichiers documentés qu'inclut directement ou indirectement chaque fichier. Une sorte de graphique de dépendances entre chaque fichier et les fichiers inclus ;
- Créerun graphique représentant la hiérarchie d'une classe, tout en incluant les dénominations textuelles. Supporté uniquement avec le format HTML !
- Créer un graphique pour chaque fonction, montrant les fonctions que la fonction appelle directement ou indirectement. Cette option n'est utilisable que si vous cochez la case « Include cross-referenced source code in the output » dans le deuxième onglet !
Voici un exemple type d'un diagramme d'appel (option « Call graphs »). Ici il s'agit de la fonction main de l'exemple de code montré plus haut :
V-E. Valider les options et les enregistrer▲
Pour valider toutes ces options, il faut en premier lieu cliquer sur le bouton OK de cette boîte de dialogue. Vous vous retrouverez ensuite sur la fenêtre principale à partir de laquelle, vous pourrez enregistrer vos options dans l'étape 2 (step 2) par le bouton Save !
Mais avant de pouvoir lancer la génération par le bouton Start, il vous faut définir le répertoire courant dans la partie 3 (step 3), en général je choisis le répertoire racine où se situe le code source.
Les options telles que présentées dans les captures d'écrans ont pour résultat la sortie suivante pour l'exemple de code en C et à quelques détails près pour l'exemple en C++.
VI. Configuration avancée▲
Je ne vais pas détailler ici toutes les options, ce serait bien trop long, mais seulement certaines que j'estime être assez intéressantes pour peaufiner les réglages et la présentation générale de la documentation. Pour accéder aux options avancées de Doxygen, il faut passer par la fenêtre principale et cliquer sur le bouton Expert qui se trouve juste après Wizard !
Chaque nom d'option dans les options avancées de Doxygen correspond en réalité aux options telles qu'elles sont écrites dans le fichier de configuration généré par le programme !
VI-A. Onglet « Project »▲
Une chose importante qu'il est bon à savoir, c'est que l'intitulé des différents paragraphes peut être généré en différentes langues, par défaut c'est en Anglais, mais nous pouvons le mettre en Français, voyez pour ça, la liste nommée OUTPUT_LANGUAGE.
D'autres options intéressantes possibles de cette page sont :
- USE_WINDOWS_ENCODING : permet de forcer l'encodage des caractères par celui utilisé par Windows si la case est cochée, sinon c'est un encodage de type Unix/Linux qui est utilisé ;
- BRIEF_MEMBER_DESC : permet d'afficher ou non la description courte dans la liste des fonctions/énumérations/structures/classes/etc. Liste qui se trouve en haut de page donc avant la partie détaillée de la documentation !
- REPEAT_BRIEF : permet d'afficher ou non la description courte dans la partie détaillée de la documentation. Cette option et la précédente sont conjointement liées, car si vous désactivez ces deux options, la description courte sera tout de même affichée dans la section détaillée de la documentation !
- DETAILS_AT_TOP : permet d'afficher la description détaillée du fichier en haut de la page si cette option est cochée.
Voilà quelques options utiles pour une utilisation plus ou moins basique de Doxygen. L'activation de ces options a pour résultat la génération suivante pour les codes d'exemples :
On peut déjà voir des changements notamment le titre en gras des paragraphes qui est désormais en français !
VI-B. Onglet « Build »▲
Dans cette section, pour peaufiner encore un peu plus nos réglages de génération, seules quelques options nous intéresseront à savoir :
- EXTRACT_PRIVATE (C++ et autres langages orientés objet) : permet de faire afficher la documentation des fonctions/méthodes et autres membres ayant le qualificateur private ;
- EXTRACT_STATIC : permet de faire afficher la documentation des fonctions/structures/énumérations/etc. ayant le qualificateur static ;
- SORT_MEMBER_DOCS : si elle est cochée, cette option permet de trier les descriptions détaillées par ordre alphabétique sinon, l'ordre est celui de leur déclaration dans le code source.
Voilà le résultat de la génération après avoir coché les options EXTRACT_STATIC et EXTRACT_PRIVATE respectivement pour le C et le C++ et décoché l'option SORT_MEMBER_DOCS :
Cocher l'option EXTRACT_ALL a le même effet qu'activer l'option « All entities » dans les options de l'assistant de configuration, sauf en ce qui concerne le style du tri !
VI-C. Onglet « HTML »▲
Vous désirez personnaliser la sortie de votre documentation ? C'est sur cet onglet que ça se passe alors ! Vous pouvez en effet ici, personnaliser l'en-tête et le pied de page de vos documentations en précisant leur chemin dans les champs HTML_HEADER et HTML_FOOTER respectivement. On peut également changer l'aspect général (le design) en précisant votre propre fichier CSS dans le champ HTML_STYLESHEET !
Dans le chapitre 5, j'avais également précisé que les générations se font par défaut dans un répertoire nommé html, c'est dans le champ HTML_OUTPUT que nous pouvons redéfinir le nom de ce répertoire, ainsi que le type de l'extension dans le champ HTML_FILE_EXTENSION !
L'option un peu plus bas DISABLE_INDEX permet de ne pas afficher l'index qu'on peut apercevoir en haut de chaque page HTML de la documentation !
VI-D. Onglet « Dot »▲
On peut également personnaliser un petit peu les graphiques générés, voici quelques options utiles.
- UML_LOOK : permet de générer des diagrammes dans le style UML.
- DOT_IMAGE_FORMAT : permet de choisir entre trois formats d'images différents soit png, jpg et gif.
- DOT_PATH : cette option peut s'avérer utile lorsque vous avez un problème de détection du module de génération de graphiques et diagrammes. Cela peut arriver lorsque ce programme n'est pas référencé dans la variable PATH, il faut donc spécifier le chemin dans ce champ !
- MAX_DOT_GRAPH_WIDTH et MAX_DOT_GRAPH_HEIGHT : permet de changer la taille maximale d'un graphe, respectivement la largeur et la hauteur.
- MAX_DOT_GRAPH_DEPTH : permet de régler la profondeur maximum d'un graphe.
- DOT_TRANSPARENT : permet de rendre le fond des images transparent, ça peut être très utile lorsqu'on personnalise l'aspect général de la documentation !
VII. Conclusion▲
Doxygen est un outil qui peut s'avérer très pratique, notamment dans des développements de gros projets et autres comme des bibliothèques de fonctions, pour en créer des documentations techniques pour d'autres développeurs !
Avec un peu d'effort, de tests et quelques petites touches personnelles, on peut arriver à des résultats dignes des documentations de GTK+ ou Glib par exemple. Voici une documentation que j'ai faite pour une de mes bibliothèques personnelles : C_Str v2.1.0 - Documentation v1.2 … ça mérite de s'attarder un peu sur ce programme non ?
VIII. Rappels des liens▲
Je vous rappelle ici les liens cités dans ce tutoriel et quelques autres encore :
IX. Remerciements▲
Merci à hiko-seijuro pour le code d'exemple en version C++ et à Aspic pour la relecture et correction de ce tutoriel !