<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>blog.freelan.org &#187; &#187; Julien Kauffmann</title>
	<atom:link href="https://blog.freelan.org/author/ereon/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.freelan.org</link>
	<description>De l&#039;informatique, des octets et des poneys.</description>
	<lastBuildDate>Fri, 04 Apr 2014 17:34:59 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>Freelan 1.0 disponible !</title>
		<link>https://blog.freelan.org/2012/04/03/freelan-1-0-disponible/</link>
		<comments>https://blog.freelan.org/2012/04/03/freelan-1-0-disponible/#comments</comments>
		<pubDate>Tue, 03 Apr 2012 13:08:44 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Freelan]]></category>
		<category><![CDATA[Logiciels]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=413</guid>
		<description><![CDATA[Ceux qui me connaissent un peu sauront que j&#8217;attendais de pouvoir écrire cet article depuis un moment. Il y a pas loin de 3 ans je commençais ce qui n&#8217;était à l&#8217;époque qu&#8217;un vague projet de &#8220;VPN peer-to-peer&#8221; pour Windows. Après plusieurs réécritures et réorientations le projet a bien évolué et j&#8217;ai la joie d&#8217;annoncer [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Ceux qui me connaissent un peu sauront que j&#8217;attendais de pouvoir écrire cet article depuis un moment.</p>
<p>Il y a pas loin de 3 ans je commençais ce qui n&#8217;était à l&#8217;époque qu&#8217;un vague projet de &#8220;VPN peer-to-peer&#8221; pour Windows. Après plusieurs réécritures et réorientations le projet a bien évolué et j&#8217;ai la joie d&#8217;annoncer aujourd&#8217;hui que Freelan est désormais disponible dans sa version 1.0, non seulement pour Windows, mais pour toutes les autres plateformes !</p>
<p>Loin d&#8217;être une fin en soi, ce jour marque au contraire le début d&#8217;un nouveau cycle de vie pour le projet : installations par différents utilisateurs, exploitation réelle en production et remontées de bogues vont désormais rythmer l&#8217;évolution des développements !</p>
<p>Je tiens à remercier très sincèrement tous ceux qui ont contribué à freelan et qui ont rendu ce jour possible. Le plus gros du boulot reste encore à venir évidemment et si d&#8217;autres souhaitent contribuer, sachez que nous cherchons toujours des volontaires pour:</p>
<ul>
<li>L&#8217;installer et le tester en environnement réel chez eux et pourquoi pas chez leurs amis, familles, etc.</li>
<li>Faire de la publicité !</li>
<li>Maintenir les installeurs/paquets &#8211; Nous maintenons pour l&#8217;instant les installeurs pour Windows et Mac OS ainsi que les paquets Debian mais il y a encore bien d&#8217;autres systèmes sur lesquels freelan peut tourner et devrait donc être empaqueté !</li>
<li>Contribuer sur le code source (ajouts de fonctionnalité, correction de bogues, etc.)</li>
<li>Gérer le site web</li>
</ul>
<p>En tout cas, si vous installez freelan chez vous ou ailleurs, n&#8217;hésitez pas à m&#8217;en faire un retour !</p>
<p>P.S : Je vous l&#8217;ai pas dit, mais ça se passe là : <a title="http://www.freelan.org" href="http://www.freelan.org">http://www.freelan.org</a> <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2012/04/03/freelan-1-0-disponible/feed/</wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Restaurer son répertoire courant lors d&#8217;une ouverture de session sous Linux</title>
		<link>https://blog.freelan.org/2011/09/07/restaurer-son-repertoire-courant-lors-dune-ouverture-de-session-sous-linux/</link>
		<comments>https://blog.freelan.org/2011/09/07/restaurer-son-repertoire-courant-lors-dune-ouverture-de-session-sous-linux/#comments</comments>
		<pubDate>Wed, 07 Sep 2011 08:06:41 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[Astuces]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[directory]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[restore]]></category>
		<category><![CDATA[shell]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=394</guid>
		<description><![CDATA[Je travaille tous les jours sur un serveur Linux. Je me suis rendu compte que chaque matin, lorsque la session de la veille a été coupée, je retape systématiquement la même commande : "cd le/repertoire/vers/mon/projet/actuel".
Taper ces quelques mots chaque matin n'est pas vraiment la mer à boire, mais c'est en revanche déjà plus pénible de devoir le refaire 15 fois dans la journée, lorsque surviennent des coupures réseaux qui me font perdre ma session...
J'ai décidé ce matin, de trouver une solution simple et légère pour palier le problème.]]></description>
				<content:encoded><![CDATA[<p>Je travaille tous les jours sur un serveur Linux. Je me suis rendu compte que chaque matin, lorsque la session de la veille a été coupée, je retape systématiquement la même commande : &#8220;cd le/repertoire/vers/mon/projet/actuel&#8221;.</p>
<p>Taper ces quelques mots chaque matin n&#8217;est pas vraiment la mer à boire, mais c&#8217;est en revanche déjà plus pénible de devoir le refaire 15 fois dans la journée, lorsque surviennent des coupures réseaux qui me font perdre ma session&#8230;</p>
<p>J&#8217;ai décidé ce matin, de trouver une solution simple et légère pour palier le problème.</p>
<h1>La solution</h1>
<p>L&#8217;idée est toute bête : à chaque changement de répertoire, je stocke le nouveau répertoire courant dans un fichier à la racine de mon &#8220;home-directory&#8221;. À l&#8217;ouverture de session, je lis ce fichier s&#8217;il existe et change le répertoire courant en conséquence.</p>
<h1>Réalisation</h1>
<p>La réalisation se divise en deux étapes :</p>
<ol>
<li>L&#8217;écriture de deux scripts simples pour sauvegarder et restaurer le répertoire courant;</li>
<li>la redéfinition de la commande &#8220;<code>cd</code>&#8221; pour qu&#8217;elle exécute les scripts précédents.</li>
</ol>
<h2>Les scripts</h2>
<p>J&#8217;ai choisi de stocker mes scripts personnels dans un répertoire nommé &#8220;<code>bin</code>&#8220;, à la racine de mon &#8220;home&#8221;. Vous pouvez bien évidemment les mettre où bon vous semble.</p>
<p>Le premier script se nomme <code>savepwd.sh</code>, et voici son contenu :</p><pre class="crayon-plain-tag">#!/bin/bash

\cd $2
echo $PWD &gt; ~/.savedpwd</pre><p>Rien de fou ici : on change le répertoire (on préfixe <code>cd</code> par un antislash pour éviter d&#8217;appeler un éventuel alias; ce qui tombe bien puisque c&#8217;est ce qu&#8217;on va faire dans la deuxième partie) et on inscrit dans le fichier <code>~/.savedpwd</code> le répertoire courant après changement.</p>
<p>On spécifie <code>$2</code> pour une raison que nous verrons plus tard.</p>
<p>Le deuxième script se nomme <code>loadpwd.sh</code> et contient les lignes suivantes :</p><pre class="crayon-plain-tag">#!/bin/bash

PWDFILE=`eval ls ~/.savedpwd 2&gt;/dev/null`

if [ -e &quot;$PWDFILE&quot; ]
then
        RESTORED_PWD=`cat $PWDFILE`
        \cd $RESTORED_PWD
fi</pre><p>Encore une fois, rien de spécial : on teste l&#8217;existence du fichier <code>~/.savedpwd</code>, et s&#8217;il existe, on remplace le répertoire courant par celui qui y est inscrit.</p>
<h2>Les alias</h2>
<p>Modifions maintenant le script de démarrage de session, dans mon cas il s&#8217;agit du fichier <code>~/.bashrc</code>, et ajoutons les lignes suivantes :</p><pre class="crayon-plain-tag"># Restore/Save current working directory on open/close
alias cd='. ~/bin/savepwd.sh dummy'
. ~/bin/loadpwd.sh</pre><p>On remplace simplement la commande &#8220;<code>cd</code>&#8221; par un appel au script <code>~/bin/savedpwd.sh</code> et on fait un appel à <code>~/bin/loadpwd.sh</code> lors du premier chargement du script.</p>
<p>Vous vous posez certainement la question, pourquoi ce paramètre &#8220;<code>dummy</code>&#8221; ? Tout simplement, si on utilisait le premier paramètre (<code>$1</code>) on a un bogue dans le cas où l&#8217;appelant a fait un appel à <code>set</code> dans sa console, et quand on appelle <code>cd</code> sans paramètres : en effet, <code>set</code> définit <code>$1</code> dans la console actuelle, et devient la valeur par défaut quand le script est appelé sans paramètre.</p>
<p>En faisant par exemple :</p><pre class="crayon-plain-tag">$ set toto
$ cd</pre><p>Au lieu de retourner à la racine du &#8220;home-directory&#8221;, on se retrouve dans le répertoire &#8220;<code>toto</code>&#8220;, s&#8217;il existe. En ajoutant le paramètre &#8220;<code>dummy</code>&#8220;, on empêche le problème de se produire en s&#8217;assurant qu&#8217;aucun paramètre par défaut n&#8217;est appelé lors de l&#8217;invocation de <code>~/bin/savepwd.sh</code>.</p>
<h1>C&#8217;est fini !</h1>
<p>Ça y est, vous pouvez relancer votre session pour prendre en compte les modifications : dès que vous changerez de répertoire courant, celui-ci sera sauvé puis restauré lors de la prochaine ouverture de session !</p>
<p>J&#8217;espère en tout cas que cette astuce pourra vous servir ! N&#8217;hésitez pas à me suggérer des améliorations ou d&#8217;éventuels problèmes (sécurité, utilisation) liés à ces scripts. Bon code ! <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/09/07/restaurer-son-repertoire-courant-lors-dune-ouverture-de-session-sous-linux/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Installation d&#8217;un serveur Git avec Apache sous Linux</title>
		<link>https://blog.freelan.org/2011/09/02/installation-dun-serveur-git-avec-apache-sous-linux/</link>
		<comments>https://blog.freelan.org/2011/09/02/installation-dun-serveur-git-avec-apache-sous-linux/#comments</comments>
		<pubDate>Fri, 02 Sep 2011 18:28:26 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Administration]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[dav]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[https]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[server]]></category>
		<category><![CDATA[serveur]]></category>
		<category><![CDATA[unix]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=386</guid>
		<description><![CDATA[Cet article a pour but de vous montrer comment mettre en place rapidement et simplement un serveur Git sous Apache, avec un ou plusieurs dépôts, et une authentification par identifiant/mot de passe.]]></description>
				<content:encoded><![CDATA[<p>Cet article a pour but de vous montrer comment mettre en place rapidement et simplement un serveur Git sous Apache, avec un ou plusieurs dépôts, et une authentification par identifiant/mot de passe.</p>
<h1>Démarrage</h1>
<p>Nous partirons du principe que vous avec un Linux moderne (j&#8217;utilise Debian, mais la démarche devrait être similaire sur n&#8217;importe quelle autre distribution).</p>
<p>Installez tout d&#8217;abord les paquets nécessaires :</p><pre class="crayon-plain-tag">$ apt-get install git-core apache2</pre><p>Vérifiez ensuite que git est installé :</p><pre class="crayon-plain-tag">$ git --version
git version 1.5.6.5</pre><p>Si vous obtenez une sortie similaire, c&#8217;est que Git est bien installé.</p>
<h1>Création du dépôt</h1>
<p>Nous allons commencer par attribuer un répertoire à nos futurs dépôts Git. J&#8217;ai personnellement choisi &#8220;<code>/home/git</code>&#8221; mais libre à vous de choisir autre chose. Notez simplement ce chemin, car nous allons y faire référence quelques fois dans les prochaines étapes.</p>
<p>Tapez les commandes suivantes :</p><pre class="crayon-plain-tag">$ mkdir -p /home/git/repositories
$ chown -R www-data: /home/git</pre><p>Où <code>www-data</code> est le nom du user sous lequel tourne le démon apache.</p>
<p>Nous placerons tous les dépôts Git dans le répertoire <code>/home/git/repositories</code>. Dans notre exemple, nous créons un dépôt nommé &#8220;<code>depot.git</code>&#8221; :</p><pre class="crayon-plain-tag">$ cd /home/git/repositories
$ mkdir depot.git
$ cd depot.git
$ git init --bare
$ git update-server-info</pre><p>La commande &#8220;<code>git init --bare</code>&#8221; créé un dépôt Git &#8220;bare&#8221;, c&#8217;est à dire un dépôt qui ne possède pas de copie de travail : c&#8217;est un dépôt de &#8220;stockage&#8221; uniquement; vous n&#8217;y ferez jamais de commit mais seulement des &#8220;push&#8221; ou des &#8220;pull&#8221;.</p>
<p>La commande &#8220;<code>git update-server-info</code>&#8221; met à jour les données du serveur dans le dépôt, pour lui permettre d&#8217;être accédé à distance.</p>
<p>Vous devrez répéter cette dernière étape pour chaque dépôt que vous souhaiterez créer. Notez que vous pouvez également créer une arborescence de répertoires pour organiser plus finement les dépôts.</p>
<h1>Paramétrage d&#8217;Apache</h1>
<p>Nous devons ensuite informer Apache de la présence de nos dépôts Git. Pour ce faire, nous créons un fichier nommé &#8220;<code>git</code>&#8221; dans le répertoire &#8220;<code>/etc/apache2/sites-available</code>&#8221; qui contient les paramètres suivants :</p><pre class="crayon-plain-tag">Alias /git/ &quot;/home/git/repositories/&quot;

&lt;Directory /home/git/repositories&gt;
        DAV on
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        Order allow,deny
        allow from all
        AuthType Basic
        AuthName &quot;Git&quot;
        AuthUserFile /home/git/passwd
        Require valid-user
&lt;/Directory&gt;</pre><p>Nous définissons une authentification de type &#8220;identifiant/mot de passe&#8221; mais libre à vous de choisir une autre méthode d&#8217;authentification.</p>
<p>Nous supposons ici que le serveur apache possède déjà au moins un &#8220;virtual host&#8221;. Si vous souhaitez par exemple limiter l&#8217;accès à un &#8220;virtual host&#8221; en particulier (utile dans le cas où l&#8217;on souhaite forcer le passage en <code>HTTPS</code> par exemple), il suffit d&#8217;imbriquer le code ci-dessus dans la déclaration de ce &#8220;virtual host&#8221;.</p>
<p>Lorsqu&#8217;une personne tentera d&#8217;accéder au sous répertoire &#8220;<code>/git/</code>&#8221; de notre serveur web, elle devra saisir un identifiant et un mot de passe ayant été renseigné dans le fichier &#8220;<code>/home/git/passwd</code>&#8220;.</p>
<p>Créons maintenant ce fichier grâce à la commande suivante :</p><pre class="crayon-plain-tag">$ htpasswd -c /home/git/passwd &lt;login utilisateur&gt;</pre><p>Le programme vous invite à saisir un mot de passe pour votre utilisateur. Créez autant d&#8217;utilisateurs que vous le souhaitez à l&#8217;aide de la même commande (omettez le paramètre &#8220;<code>-c</code>&#8221; lors des fois suivantes).</p>
<p>Vérifiez que l&#8217;utilisateur <code>www-data</code> ait accès à ce fichier, uniquement en lecture.</p>
<p>Vous n&#8217;avez plus qu&#8217;à activer le site, les modules <code>dav</code> et <code>dav_fs</code> puis à redémarrer apache pour mettre en ligne le dépôt :</p><pre class="crayon-plain-tag">$ a2ensite git
$ a2enmod dav
$ a2enmod dav_fs
$ /etc/init.d/apache2 restart</pre><p>Le dépôt devrait désormais, être accessible.</p>
<h1>Un petit test pour la route</h1>
<p>Testons l&#8217;accès au dépôt depuis un autre poste sur le réseau. Vous pouvez bien évidemment utiliser un poste Windows ou Linux, graphique ou en ligne de commande.</p>
<p>Pour la simplicité de l&#8217;exemple (et parce que j&#8217;adore ça), nous utiliserons la ligne de commande :</p><pre class="crayon-plain-tag">$ git clone http://notre_serveur/git/depot.git</pre><p>Cette commande devrait réussir et créer un répertoire nommé &#8220;<code>depot</code>&#8221; dans le répertoire courant.</p>
<p>Ignorez l&#8217;avertissement qui dit que le dépôt est vide : cela est tout à fait normal.</p>
<p>Remarque : si vous avez choisi d&#8217;héberger votre dépôt en HTTPS, git refusera peut-être de s&#8217;y connecter si votre certificat n&#8217;est pas signé par une autorité de certification reconnue. Vous pouvez définir la variable d&#8217;environnement &#8220;<code>GIT_SSL_NO_VERIFY=1</code>&#8221; pour ignorer l&#8217;erreur <strong>temporairement</strong>. Ne vous servez évidemment pas de cette variable comme solution <strong>à long terme</strong> !</p>
<h1>Conclusion</h1>
<p>La mise en place d&#8217;un serveur Git accessible sous Apache est donc plutôt simple. Notre configuration ici est minimale et pourrait encore être améliorée, notamment en permettant une affectation des droits plus précise pour les différents dépôts au sein d&#8217;Apache.</p>
<p>Vos recherches sur l&#8217;Internet vous auront peut-être conduit à d&#8217;autres solutions, utilisant gitosis ou encore ssh. Ces approches proposent d&#8217;autres modes d&#8217;authentification (par certificat ou en ssh avec un compte sur le système) qui peuvent être très intéressantes mais je n&#8217;ai personnellement pas réussi à les faire fonctionner correctement pour l&#8217;instant. Si vous avez des ressources à partager à ce sujet, n&#8217;hésitez pas à me les transmettre <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley" /></p>
<p>J&#8217;espère en tout cas que cet article vous aura été utile, et comme d&#8217;habitude, n&#8217;hésitez pas à me faire part de vos commentaires ! <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/09/02/installation-dun-serveur-git-avec-apache-sous-linux/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Quelques astuces Git</title>
		<link>https://blog.freelan.org/2011/09/01/quelques-astuces-git/</link>
		<comments>https://blog.freelan.org/2011/09/01/quelques-astuces-git/#comments</comments>
		<pubDate>Thu, 01 Sep 2011 09:21:23 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Astuces]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[astuces]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=372</guid>
		<description><![CDATA[Ce petit article rapide a pour but de me servir de mémo pour quelques astuces Git que j'ai découvertes récemment et qui serviront peut-être à d'autres personnes.]]></description>
				<content:encoded><![CDATA[<p>Ce petit article rapide a pour but de me servir de mémo pour quelques astuces Git que j&#8217;ai découvertes récemment et qui serviront peut-être à d&#8217;autres personnes.</p>
<h1>Supprimer tous les fichiers non-versionnés</h1>
<p>J&#8217;ai pris l&#8217;habitude dans mes projets (surtout ceux en C++) de faire un &#8220;<code>scons -c</code>&#8221; de temps en temps pour supprimer les fichiers issus de la compilation. Cependant, cette commande ne supprime évidemment pas tous les autres fichiers, eux-aussi générés mais qui proviennent d&#8217;autre part (par exemple le &#8220;<code>.sconsign.dblite</code>&#8221; généré par SCons).</p>
<p>Si votre projet utilise Git, vous pouvez aussi faire :</p><pre class="crayon-plain-tag">git clean</pre><p>Qui supprime tous les fichiers non-versionnés du dépôt.</p>
<p>Notez que par défaut, la configuration de Git prévient l&#8217;utilisation de &#8220;<code>git clean</code>&#8220;, en obligeant la spécification du paramètre &#8220;<code>-f</code>&#8220;.</p>
<p>Vous devrez donc probablement faire :</p><pre class="crayon-plain-tag">git clean -f</pre><p>Pour que ça fonctionne.</p>
<p>Pour ma part, j&#8217;ai créé l&#8217;alias suivant :</p>
<p></p><pre class="crayon-plain-tag">git config --global alias.cl 'clean -f -x -d'</pre><p></p>
<p>Qui me supprime du dépôt tous les fichiers non-versionnés, ignorés et les répertoires vides.</p>
<h1>Ajouter tous les fichiers non-versionnés au .gitignore</h1>
<p>Ayant récemment du travailler sur un projet automake/autoconf, j&#8217;ai été confronté au problème suivant :</p>
<p>automake/autoconf génèrent tout un tas de fichiers qui ne doivent pas être versionnés, et qui ont donc tout intérêt à être ignorés.</p>
<p>La commande git status m&#8217;affichait quelque-chose de ce genre :</p><pre class="crayon-plain-tag"># On branch master
# Untracked files:

# &nbsp; (use &quot;git add &amp;lt;file&amp;gt;...&quot; to include in what will be committed)
#
# &nbsp; &nbsp; &nbsp; Makefile.global
# &nbsp; &nbsp; &nbsp; acinclude.m4
# &nbsp; &nbsp; &nbsp; aclocal.m4
#       autom4te.cache/
#       build/
#       config.guess
#       config.h.in
#       config.sub
#       configure
#       configure.in
#       install-sh
#       ltmain.sh
#       missing
#       mkinstalldirs
#       run-tests.php
nothing added to commit but untracked files present (use &quot;git add&quot; to track)</pre><p>Très informatif, certes, mais peu exploitable. Et je n&#8217;avais pas envie de jouer du parseur pour traiter une liste de quelques fichiers.</p>
<p>Notez que dans ce cas, vous pouvez simplement utiliser :</p><pre class="crayon-plain-tag">git ls-files -o &amp;gt; .gitignore</pre><p>Qui va ajouter dans le fichier .gitignore tous les fichiers non-versionnés <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>Je vous invite par ailleurs à regarder l&#8217;aide de la commande git ls-files pour voir toutes les options d&#8217;affichage qu&#8217;elle propose : une vraie mine d&#8217;or !</p>
<h1>Mais encore&#8230;</h1>
<p>N&#8217;hésitez pas à commenter si avez vous aussi des astuces à partager. Je me ferai une joie de mettre à jour cet article !</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/09/01/quelques-astuces-git/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Boost::Python, dates et conversions</title>
		<link>https://blog.freelan.org/2011/08/09/boostpython-dates-et-conversions/</link>
		<comments>https://blog.freelan.org/2011/08/09/boostpython-dates-et-conversions/#comments</comments>
		<pubDate>Tue, 09 Aug 2011 17:54:20 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Boost]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[boost]]></category>
		<category><![CDATA[boost::posix_time]]></category>
		<category><![CDATA[boost::python]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[conversion]]></category>
		<category><![CDATA[date]]></category>
		<category><![CDATA[datetime]]></category>
		<category><![CDATA[heure]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[time]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=354</guid>
		<description><![CDATA[Ceux qui ont déjà joué avec Boost::Python le savent : cette API est très complète mais malheureusement trop peu documentée. Si elle permet de rendre accessible du code C++ depuis un environnement Python avec parfois une déconcertante simplicité, certaines tâches triviales sont en revanche plus difficiles à réaliser qu'on aurait pu l'imaginer.
Si vous êtes sur cette page, c'est très probablement parce que vous aussi devez jouer avec des dates et des durées, de C++ vers Python, inversement ou bien les deux et que vous vous êtes vous aussi heurté à la complexité de Boost::Python.
L'article qui suit est le résultat de mes recherches en la matière, et la synthèse de la solution à laquelle je suis parvenu.]]></description>
				<content:encoded><![CDATA[<p>Ceux qui ont déjà joué avec Boost::Python le savent : cette API est très complète mais malheureusement trop peu documentée. Si elle permet de rendre accessible du code C++ depuis un environnement Python avec parfois une déconcertante simplicité, certaines tâches triviales sont en revanche plus difficiles à réaliser qu&#8217;on aurait pu l&#8217;imaginer.</p>
<p>Si vous êtes sur cette page, c&#8217;est très probablement parce que vous aussi devez jouer avec des dates et des durées, de C++ vers Python, inversement ou bien les deux et que vous vous êtes vous aussi heurté à la complexité de Boost::Python.</p>
<p>L&#8217;article qui suit est le résultat de mes recherches en la matière, et la synthèse de la solution à laquelle je suis parvenu.</p>
<h1>Le code de base</h1>
<p>Pour l&#8217;ensemble de l&#8217;article, nous partirons du principe que vous utilisez la classe <code>boost::posix_time::ptime</code> côté C++, et <code>datetime.datetime</code> côté Python.</p>
<p><em>Remarque : Si jamais vous utilisiez une autre classe pour vos dates (quelle soit &#8220;maison&#8221; ou issue d&#8217;une autre bibliothèque), vous devriez pouvoir adapter le principe sans trop de problèmes.</em></p>
<p>Le code minimal pour un module Python avec Boost::Python dont nous partirons est le suivant :</p>
<p></p><pre class="crayon-plain-tag">/**
* \file module.cpp
* \author Julien Kauffmann
* \brief The Python module file.
*/

#include &lt;boost/python/module.hpp&gt;

BOOST_PYTHON_MODULE(module)
{
}</pre><p></p>
<p>Rien de fou donc, pour l&#8217;instant.</p>
<p>Ajoutons une fonction qui prend en paramètre et retourne un <code>boost::posix_time::ptime</code> :</p><pre class="crayon-plain-tag">/**
 * \file module.cpp
 * \author Julien Kauffmann
 * \brief The Python module file.
 */

#include &lt;boost/python/module.hpp&gt;
#include &lt;boost/python/def.hpp&gt;

#include &lt;boost/date_time/posix_time/posix_time.hpp&gt;

boost::posix_time::ptime add_five_seconds(boost::posix_time::ptime ptime)
{
  if (!ptime.is_special())
  {
    return ptime + boost::posix_time::seconds(5);
  }

  return ptime;
}

BOOST_PYTHON_MODULE(module)
{
  boost::python::def(&quot;add_five_seconds&quot;, &amp;add_five_seconds, boost::python::args(&quot;ptime&quot;), &quot;Add five seconds to a datetime then return the result&quot;);
}</pre><p>Cette fonction ajoute bêtement 5 secondes à toute date qui lui est passée, sauf si celle-ci n&#8217;est pas une date valide.</p>
<p>Si le code actuel compile et donne une bibliothèque Python valide, l&#8217;appel de <code>add_five_seconds()</code> depuis l&#8217;interpréteur Python provoque la levée d&#8217;une exception :</p><pre class="crayon-plain-tag">Python 2.4.3 (#1, Apr 14 2011, 20:41:59)
Type &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.

In [1]: import datetime, module

In [2]: module.add_five_seconds(datetime.datetime.today())
---------------------------------------------------------------------------
ArgumentError                             Traceback (most recent call last)

/home/ereon/workbench/pythondate/

ArgumentError: Python argument types in
    module.add_five_seconds(datetime.datetime)
did not match C++ signature:
    add_five_seconds(boost::posix_time::ptime ptime)</pre><p>En effet : à aucun moment nous n&#8217;avons indiqué à Boost::Python comment convertir une date Python en date C++, ni même que cette conversion était possible.</p>
<p>Pour que ceci fonctionne, il faut ajouter deux &#8220;converters&#8221; : un de C++ vers Python, et l&#8217;autre de Python vers C++.</p>
<h2>Conversion de C++ vers Python</h2>
<p>Si Boost::Python ne prend pas nativement en charge la conversion de date, il fournit néanmoins des outils puissants pour nous permettre d&#8217;y arriver.</p>
<p>Ajoutons le code de conversion à notre exemple précédent :</p><pre class="crayon-plain-tag">/**
 * \file module.cpp
 * \author Julien Kauffmann &lt;julien.kauffmann@freelan.org&gt;
 * \brief The Python module file.
 */

#include &lt;boost/python/module.hpp&gt;
#include &lt;boost/python/def.hpp&gt;
#include &lt;boost/python/class.hpp&gt;

#include &lt;boost/date_time/posix_time/posix_time.hpp&gt;

#include &lt;datetime.h&gt;

boost::posix_time::ptime add_five_seconds(boost::posix_time::ptime ptime)
{
  if (!ptime.is_special())
  {
    return ptime + boost::posix_time::seconds(5);
  }

  return ptime;
}

struct date_to_python_converter
{
  static PyObject* convert(boost::posix_time::ptime value)
  {
    if (value.is_not_a_date_time())
      return Py_None;

    PyDateTime_IMPORT;

    return PyDateTime_FromDateAndTime(
      static_cast&lt;int&gt;(value.date().year()),
      static_cast&lt;int&gt;(value.date().month()),
      static_cast&lt;int&gt;(value.date().day()),
      static_cast&lt;int&gt;(value.time_of_day().hours()),
      static_cast&lt;int&gt;(value.time_of_day().minutes()),
      static_cast&lt;int&gt;(value.time_of_day().seconds()),
      static_cast&lt;int&gt;(value.time_of_day().total_microseconds() - value.time_of_day().total_seconds() * 1000000L)
      );
  }
};

BOOST_PYTHON_MODULE(module)
{
  boost::python::to_python_converter&lt;boost::posix_time::ptime, date_to_python_converter&gt;();

  boost::python::def(&quot;add_five_seconds&quot;, &amp;add_five_seconds, boost::python::args(&quot;ptime&quot;), &quot;Add five seconds to a datetime then return the result&quot;);
}</pre><p></p>
<h3>Analyse</h3>
<p>Regardons ligne par ligne les changements apportés.</p><pre class="crayon-plain-tag">#include &lt;boost/python/class.hpp&gt;
#include &lt;datetime.h&gt;</pre><p>Le premier include est nécessaire pour utiliser <code>boost::python::to_python_converter</code>; le second pour rendre disponibles les types Python natifs, tels que <code>PyObject</code> ou <code>PyDateTime</code>.</p><pre class="crayon-plain-tag">struct date_to_python_converter
{
  static PyObject* convert(boost::posix_time::ptime value)
  {
    if (value.is_not_a_date_time())
      return Py_None;

    PyDateTime_IMPORT;

    return PyDateTime_FromDateAndTime(
      static_cast&lt;int&gt;(value.date().year()),
      static_cast&lt;int&gt;(value.date().month()),
      static_cast&lt;int&gt;(value.date().day()),
      static_cast&lt;int&gt;(value.time_of_day().hours()),
      static_cast&lt;int&gt;(value.time_of_day().minutes()),
      static_cast&lt;int&gt;(value.time_of_day().seconds()),
      static_cast&lt;int&gt;(value.time_of_day().total_microseconds() - value.time_of_day().total_seconds() * 1000000L)
      );
  }
};</pre><p>Ici nous déclarons un &#8220;converter&#8221;. Au sens de Boost::Python, un &#8220;converter&#8221; est une simple structure ou classe qui contient une méthode statique nommée <code>convert()</code> qui prend un paramètre le type natif C++ à convertir, et qui retourne un <code>PyObject*</code>.</p>
<p>Dans le cas où notre date n&#8217;est pas une date valide, nous choisissons ici de renvoyer <code>None</code>. Libre à vous de modifier ce comportement pour satisfaire vos propres besoins.</p>
<p>Remarque : La valeur retournée <strong>doit</strong> avoir un compteur de référence <strong>strictement positif</strong>.</p><pre class="crayon-plain-tag">boost::python::to_python_converter&lt;boost::posix_time::ptime, date_to_python_converter&gt;();</pre><p>Enfin nous déclarons notre &#8220;converter&#8221; en spécifiant le type natif et la structure/classe à utiliser pour la conversion.</p>
<p>À partir de ce moment là, notre module Python sera capable de convertir implicitement tout <code>boost::posix_time::ptime</code> en <code>datetime.datetime</code> Python.</p>
<h2>Conversion de Python vers C++</h2>
<p>La conversion dans l&#8217;autre sens demande un peu plus de travail :</p><pre class="crayon-plain-tag">/**
 * \file module.cpp
 * \author Julien Kauffmann &lt;julien.kauffmann@freelan.org&gt;
 * \brief The Python module file.
 */

#include &lt;boost/python/module.hpp&gt;
#include &lt;boost/python/def.hpp&gt;
#include &lt;boost/python/class.hpp&gt;

#include &lt;boost/date_time/posix_time/posix_time.hpp&gt;

#include &lt;datetime.h&gt;

boost::posix_time::ptime add_five_seconds(boost::posix_time::ptime ptime)
{
  if (!ptime.is_special())
  {
    return ptime + boost::posix_time::seconds(5);
  }

  return ptime;
}

struct date_to_python_converter
{
  static PyObject* convert(boost::posix_time::ptime value)
  {
    if (value.is_not_a_date_time())
      return Py_None;

    PyDateTime_IMPORT;

    return PyDateTime_FromDateAndTime(
        static_cast&lt;int&gt;(value.date().year()),
        static_cast&lt;int&gt;(value.date().month()),
        static_cast&lt;int&gt;(value.date().day()),
        static_cast&lt;int&gt;(value.time_of_day().hours()),
        static_cast&lt;int&gt;(value.time_of_day().minutes()),
        static_cast&lt;int&gt;(value.time_of_day().seconds()),
        static_cast&lt;int&gt;(value.time_of_day().total_microseconds() - value.time_of_day().total_seconds() * 1000000L)
        );
  }
};

struct date_from_python_converter
{
  static void* is_convertible(PyObject* obj_ptr)
  {
    assert(obj_ptr);

    if (obj_ptr == Py_None)
      return obj_ptr;

    PyDateTime_IMPORT;

    if (PyDateTime_Check(obj_ptr))
      return obj_ptr;

    return NULL;
  }

  static void convert(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data)
  {
    assert(obj_ptr);

    void* const storage = reinterpret_cast&lt;boost::python::converter::rvalue_from_python_storage&lt;boost::posix_time::ptime&gt;*&gt;(data)-&gt;storage.bytes;

    if (obj_ptr == Py_None)
    {
      new (storage) boost::posix_time::ptime();
    } else
    {
      PyDateTime_IMPORT;
      PyDateTime_DateTime* dt_ptr = reinterpret_cast&lt;PyDateTime_DateTime*&gt;(obj_ptr);

      const int year = PyDateTime_GET_YEAR(dt_ptr);
      const int month = PyDateTime_GET_MONTH(dt_ptr);
      const int day = PyDateTime_GET_DAY(dt_ptr);
      const int hour  = PyDateTime_DATE_GET_HOUR(dt_ptr);
      const int minute  = PyDateTime_DATE_GET_MINUTE(dt_ptr);
      const int second = PyDateTime_DATE_GET_SECOND(dt_ptr);
      const int microsecond = PyDateTime_DATE_GET_MICROSECOND(dt_ptr);

      new (storage) boost::posix_time::ptime(boost::gregorian::date(year, month, day), boost::posix_time::time_duration(hour, minute, second, 0) + boost::posix_time::microseconds(microsecond));
    }

    data-&gt;convertible = storage;
  }
};

BOOST_PYTHON_MODULE(module)
{
  boost::python::to_python_converter&lt;boost::posix_time::ptime, date_to_python_converter&gt;();

  boost::python::converter::registry::push_back(&amp;date_from_python_converter::is_convertible, &amp;date_from_python_converter::convert, boost::python::type_id&lt;boost::posix_time::ptime&gt;());  

  boost::python::def(&quot;add_five_seconds&quot;, &amp;add_five_seconds, boost::python::args(&quot;ptime&quot;), &quot;Add five seconds to a datetime then return the result&quot;);
}</pre><p></p>
<h3>Analyse</h3>
<p>Regardons encore une fois, ligne par ligne les modifications apportées :</p><pre class="crayon-plain-tag">struct date_from_python_converter</pre><p>Nous ajoutons une structure qui va contenir les routines de conversions. Contrairement à tout à l&#8217;heure, et comme nous le verrons plus tard, ceci n&#8217;est pas indispensable. Pour effectuer des conversions de Python vers C++, Boost::Python a juste besoin de deux fonctions. Que celles-ci soient statiques au sein d&#8217;une classe ou libres n&#8217;a aucune incidence.</p><pre class="crayon-plain-tag">static void* is_convertible(PyObject* obj_ptr)
  {
    assert(obj_ptr);

    if (obj_ptr == Py_None)
      return obj_ptr;

    PyDateTime_IMPORT;

    if (PyDateTime_Check(obj_ptr))
      return obj_ptr;

    return NULL;
  }</pre><p>La fonction <code>is_convertible()</code> sera utilisée par Boost::Python pour déterminer si l&#8217;instance Python à convertir peut l&#8217;être.</p>
<p>Dans notre cas nous acceptons tout d&#8217;abord <code>None</code> comme valeur &#8220;valide&#8221; pour respecter la symétrie, puis nous testons si l&#8217;instance est de type <code>datetime.datetime</code> grâce à la fonction <code>PyDateTime_Check()</code>.</p>
<p>En cas de succès, nous renvoyons la valeur passée en paramètre telle quelle. En cas d&#8217;erreur, nous renvoyons <code>NULL</code>.</p><pre class="crayon-plain-tag">static void convert(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data)
  {
    assert(obj_ptr);

    void* const storage = reinterpret_cast&lt;boost::python::converter::rvalue_from_python_storage&lt;boost::posix_time::ptime&gt;*&gt;(data)-&gt;storage.bytes;

    if (obj_ptr == Py_None)
    {
      new (storage) boost::posix_time::ptime();
    } else
    {
      PyDateTime_IMPORT;
      PyDateTime_DateTime* dt_ptr = reinterpret_cast&lt;PyDateTime_DateTime*&gt;(obj_ptr);

      const int year = PyDateTime_GET_YEAR(dt_ptr);
      const int month = PyDateTime_GET_MONTH(dt_ptr);
      const int day = PyDateTime_GET_DAY(dt_ptr);
      const int hour  = PyDateTime_DATE_GET_HOUR(dt_ptr);
      const int minute  = PyDateTime_DATE_GET_MINUTE(dt_ptr);
      const int second = PyDateTime_DATE_GET_SECOND(dt_ptr);
      const int microsecond = PyDateTime_DATE_GET_MICROSECOND(dt_ptr);

      new (storage) boost::posix_time::ptime(boost::gregorian::date(year, month, day), boost::posix_time::time_duration(hour, minute, second, 0) + boost::posix_time::microseconds(microsecond));
    }

    data-&gt;convertible = storage;
  }</pre><p>C&#8217;est ici que se passe le gros du travail : lorsque cette fonction est appelée, cela signifie que l&#8217;instance <code>obj_ptr</code> a passé l&#8217;appel à <code>is_convertible()</code> et est prête à être convertie.</p>
<p>Le paramètre <code>data</code> lui contient entre autres l&#8217;adresse de la zone mémoire où nous devons instancier notre résultat de conversion. Notez que pour ce faire, nous utilisons le &#8220;placement new&#8221; qui permet de construire une instance à un emplacement mémoire donné. Le <code>delete</code> correspondant sera automatiquement appelé par Boost::Python au besoin.</p>
<p>Pour finir, nous renseignons le champ <code>convertible</code> du paramètre <code>data</code> pour y indiquer où nous avons alloué notre résultat.</p><pre class="crayon-plain-tag">boost::python::converter::registry::push_back(&amp;date_from_python_converter::is_convertible, &amp;date_from_python_converter::convert, boost::python::type_id&lt;boost::posix_time::ptime&gt;());</pre><p>Comme auparavant, la dernière étape consiste à enregistrer le &#8220;converter&#8221; pour le faire connaître de Boost::python.<br />
On remarque ici que comme énoncé précédemment, l&#8217;appel prend en paramètre deux fonctions qui n&#8217;ont pas nécessairement besoin de faire partie d&#8217;une classe ou d&#8217;une structure.</p>
<h1>Résultat</h1>
<p>Compilez le code ci-dessus (voir le script <em>SConstruct</em> en annexe), puis chargez votre module au sein de l&#8217;interpreteur Python :</p><pre class="crayon-plain-tag">Python 2.4.3 (#1, Apr 14 2011, 20:41:59)
Type &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.

In [1]: import datetime, module

In [2]: d = datetime.datetime.now()

In [3]: print d
2011-08-09 11:30:43.239460

In [4]: print module.add_five_seconds(d)
2011-08-09 11:30:48.239460</pre><p>Ça y est ! La conversion <code>boost::posix_time::ptime</code> &lt;=&gt; <code>datetime.datetime</code> fonctionne parfaitement. <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<h1>Conclusion</h1>
<p>Boost::Python est définitivement une bibliothèque très puissante. On peut certes regretter la qualité de sa documentation, mais fort heureusement les ressources à son sujet sur l&#8217;Internet ne manquent pas. Son extensibilité la rend utilisable dans toutes les situations et facilite grandement la vie du développeur.</p>
<p>J&#8217;espère que cet article vous aura aidé et/ou donné envie de découvrir/utiliser Boost::Python. Comme toujours, n&#8217;hésitez pas à me signaler toute coquille, erreur ou optimisation qui m&#8217;aurait échappé.</p>
<h1>Annexe</h1>
<p>Voici le script SConstruct que j&#8217;ai utilisé pour la compilation :</p><pre class="crayon-plain-tag">import os

env = Environment(ENV = os.environ.copy())
env['CPPPATH'] = ['/usr/include/python2.4']

module = env.SharedLibrary(source = Glob('*.cpp'), target = 'python/module.so', SHLIBPREFIX='', LIBS = ['boost_python', 'python2.4'])</pre><p></p>
<h1>Sources</h1>
<p>Ces pages m&#8217;ont été très utiles lors de la rédaction de cet article :</p>
<ul>
<li>L&#8217;API datetime sur <a href="http://docs.python.org/c-api/datetime.htmlhttp://docs.python.org/c-api/datetime.html">python.org</a> (en Anglais);</li>
<li>l&#8217;API Boost::Posix Time sur <a href="http://www.boost.org/doc/libs/1_47_0/doc/html/date_time/posix_time.html">boost.org</a> (en Anglais);</li>
<li>l&#8217;API de Boost::Python sur <a href="http://www.boost.org/doc/libs/1_46_0/libs/python/doc/index.html">boost.org</a> (en Anglais);</li>
<li>cet <a href="http://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/">article</a> de misspent (en Anglais).</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/08/09/boostpython-dates-et-conversions/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>L&#8217;héritage en C++</title>
		<link>https://blog.freelan.org/2011/03/08/lheritage-en-c/</link>
		<comments>https://blog.freelan.org/2011/03/08/lheritage-en-c/#comments</comments>
		<pubDate>Tue, 08 Mar 2011 17:40:01 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[cast]]></category>
		<category><![CDATA[conversion]]></category>
		<category><![CDATA[héritage]]></category>
		<category><![CDATA[inheritance]]></category>
		<category><![CDATA[privé]]></category>
		<category><![CDATA[protégé]]></category>
		<category><![CDATA[public]]></category>
		<category><![CDATA[virtuel]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=327</guid>
		<description><![CDATA[Tous les langages modernes offrent aujourd'hui, par leur nature orientée objet, un moyen puissant de réutiliser du code existant : l'héritage. La plupart des programmeurs sont bien entendu déjà familiers avec le principe de dériver d'une classe pour en étendre les possibilités (ou les restreindre) dans une classe fille.
Cependant, dans ce domaine, C++ va un peu plus loin que les autres langages en proposant différentes notions d'héritage public, privé, protégé et virtuel. Nous allons tenter, dans cet article, d'expliquer en détails leurs rôles et en quoi elles peuvent se rendre utiles.]]></description>
				<content:encoded><![CDATA[<p>Tous les langages modernes offrent aujourd&#8217;hui, par leur nature orientée objet, un moyen puissant de concevoir des hiérarchies : l&#8217;héritage. La plupart des programmeurs sont bien entendu déjà familiers avec le principe de dériver d&#8217;une classe pour en étendre les possibilités (ou les restreindre) dans une classe fille. Cependant, dans ce domaine, C++ va un peu plus loin que les autres langages en proposant différentes notions d&#8217;héritage public, privé, protégé et virtuel. Nous allons tenter, dans cet article, d&#8217;expliquer en détails leurs rôles et en quoi elles peuvent se rendre utiles.</p>
<h1>Un cas classique</h1>
<p></p><pre class="crayon-plain-tag">class Base
{
  public:
    int x() const { return m_x; }
  protected:
    void set_x(int value) { m_x = value; }
  private:
    int m_x;
};

class Derived : public Base
{
  public:
    void foo() { set_x(5); }
};</pre><p>Ce code n&#8217;évoque probablement rien de nouveau pour la plupart d&#8217;entre vous : nous déclarons une classe <code>Base</code>, qui possède une méthode <em>publique</em>, une méthode <em>protégée</em> et une variable membre <em>privée</em>. Nous déclarons également une classe <code>Derived</code>, qui <em>dérive</em> de <code>Base</code> au travers d&#8217;un <strong>héritage public</strong>. Ce principe d&#8217;héritage public est généralement simple à comprendre et il est présent dans pratiquement tous les langages objets : lorsque <code>Derived</code> dérive de <code>Base</code> au travers d&#8217;un <em>héritage public</em>, on dit aussi que <code>Derived</code> &#8220;<em>est un&#8221;</em> <code>Base</code>. Toute instance de <code>Derived</code> peut être considérée comme une instance de <code>Base</code>. Comme nous l&#8217;avions vu, <a href="http://blog.freelan.org/2011/01/17/les-casts-en-cpp/">dans un article précédent</a>, il est également possible de tester si une instance de <code>Base</code> est une instance de <code>Derived</code> grâce à un <code>dynamic_cast<></code>. De façon résumée, un héritage publique offre les garanties suivantes :</p>
<ul>
<li>Toutes les membres (variables et méthodes) <strong>publics</strong> dans la classe de base le sont aussi dans la classe dérivée.</li>
<li>Toutes les membres <strong>protégés</strong> dans la classe de base le sont aussi dans la classe dérivée.</li>
<li>Les membres <strong>privés</strong> de la classe de base ne sont pas accessibles à la classe dérivée.</li>
<li>On peut convertir <em>implicitement</em> un pointeur (respectivement une référence) sur la classe dérivée vers un pointeur (respectivement une référence) sur la classe de base.</li>
<li>On peut convertir <em>explicitement</em> un pointeur (respectivement une référence) sur la classe de base vers un pointeur (respectivement une référence) sur la classe dérivée.</li>
</ul>
<p>Ainsi dans notre exemple, <code>Derived</code> peut, dans sa méthode <code>foo()</code> (et <strong>indépendamment de la visibilité de cette méthode</strong>), appeler la méthode protégée de son parent, <code>set_x()</code>. Elle ne peut pas en revanche, directement accéder à <code>m_x</code> qui est dans une <strong>section privée</strong> de la classe de base.</p>
<h2>Le rôle de l&#8217;héritage public</h2>
<p>Une concept erroné va généralement de pair avec l&#8217;utilisation de l&#8217;héritage public : il consiste à dire que ce type d&#8217;héritage est utilisé principalement pour favoriser la <strong>réutilisation de code</strong>. Ceci n&#8217;est pas (ou ne devrait pas) être la raison : en C++, il existe beaucoup de moyens de réutiliser son code, la façon la plus efficace étant la <strong>composition</strong>. Le fait de d&#8217;utiliser un héritage public <em>doit</em> être une <strong>décision fonctionnelle</strong>, et pas une <strong>décision technique</strong> : ainsi, la classe <code>Table</code> dérive de la classe <code>Meuble</code> parce qu&#8217;une table <em>&#8220;est un&#8221;</em> meuble, et pas parce qu&#8217;en faisant ainsi on évite de retaper du code. L&#8217;héritage (public ou non) s&#8217;il apporte quelque-chose, c&#8217;est principalement de la <strong>flexibilité</strong>.</p>
<h2>Bonnes pratiques</h2>
<p>Certains auront peut être pensé la chose suivante : &#8220;La méthode <code>set_x()</code> ne sert à rien. Autant déclarer <code>m_x</code> <em>protégé</em> directement : il y aura moins de code&#8221;. Si cette remarque part surement d&#8217;une bonne intention (après tout, avoir moins de code est en général une très bonne idée), elle risque ici d&#8217;avoir des conséquences fâcheuses : En procédant de la même façon que dans l&#8217;exemple, nous ajoutons certes une méthode, mais nous réduisons également le <strong>couplage</strong>. En optant pour la solution &#8220;simplifiée&#8221;, si pour une raison ou pour un autre, je suis amené à modifier l&#8217;implémentation de <code>Base</code> et à renommer par exemple <code>m_x</code> en <code>m_first_x</code>, je devrais alors modifier non seulement tout le code de <code>Base</code> mais aussi tout le code de <code>Derived</code> (puisque nous y faisons référence à <code>m_x</code> directement). Le fait d&#8217;ajouter une méthode protégée à <code>Base</code> permet de figer son interface &#8220;publique&#8221; et réduit donc drastiquement le couplage. Il en résulte un code bien plus maintenable. En règle générale, on retiendra que les variables au sein d&#8217;une classe seront toujours soit <strong>publiques</strong>, soit <strong>privées</strong>, mais très rarement <strong>protégées</strong>. Et si dans un cas particulier vous sentez avoir vraiment besoin d&#8217;un accès à un membre privé, préférez la directive <code>friend</code> qui augmentera bien moins le couplage.</p>
<p>Cette règle n&#8217;est évidemment pas absolue et vous risquez de rencontrer un scénario où déclarer une variable membre <strong>protégée</strong> est la bonne chose à faire. Cependant, ça ne devrait logiquement pas être votre premier réflexe.</p>
<h1>L&#8217;héritage privé : une alternative à la composition</h1>
<p>Qui n&#8217;a jamais écrit par erreur, au cours d&#8217;une soirée (nuit ?) un peu trop longue quelque-chose de ce genre :</p><pre class="crayon-plain-tag">class Derived : Base
{
};</pre><p>Il en résulte en général de longues minutes très agaçantes où l&#8217;on tente de comprendre pourquoi le compilateur refuse systématiquement d&#8217;utiliser les variables de la classe parente. La raison est plutôt simple : en l&#8217;absence d&#8217;un attribut de visibilité, l&#8217;héritage par défaut en C++ est <strong>privé</strong>. Le code précédent est donc équivalent à :</p><pre class="crayon-plain-tag">class Derived : private Base
{
};</pre><p>Voilà une bonne occasion d&#8217;expliquer à quoi sert ce type d&#8217;héritage. L&#8217;héritage privé offre les garanties suivantes :</p>
<ul>
<li>Tous les membres <strong>publics</strong> dans la classe de base sont <strong>privés</strong> dans la classe dérivée.</li>
<li>Tous les membres <strong>protégés</strong> dans la classe de base sont <strong>privés</strong> dans la classe dérivée.</li>
<li>Les membres <strong>privés</strong> de la classe de base ne sont pas accessibles à la classe dérivée.</li>
<li>La classe dérivée peut redéfinir les méthodes <strong>virtuelles</strong> de la classe de base.</li>
<li>Toutes les méthodes de la classe dérivée peuvent convertir un pointeur (respectivement une référence) sur <code>Derived</code> en pointeur (respectivement une référence) sur <code>Base</code>. <strong>Ceci n&#8217;est en revanche normalement pas possible en dehors de la classe dérivée.</strong> (Voir tout de même la remarque).</li>
</ul>
<p>Là où un héritage <em>public</em> traduit une relation de type &#8220;est un&#8221;, un héritage <em>privé</em> lui traduit une relation de type &#8220;est implémenté en tant que&#8221;. En pratique, un héritage privé est comparable à une composition de type 1-vers-1. Prenons un exemple plus parlant :</p><pre class="crayon-plain-tag">class Engine
{
  public:
    void start();
};

class Car : private Engine
{
  public:
    using Engine::start;
};</pre><p>Nous avons déclaré une classe <code>Engine</code> (moteur) et <code>Car</code> (voiture) qui en dérive de façon privée. Dans la déclaration de <code>Car</code>, nous indiquons, au sein d&#8217;une section publique et grâce au mot clé <code>using</code>, que nous <em>utilisons</em> la méthode <code>start()</code> de la classe parente. Celle-ci devient donc publique pour la classe fille et en dehors. Dans ce cas, on voit bien qu&#8217;un héritage public n&#8217;aurait aucun sens puisqu&#8217;une voiture &#8220;n&#8217;est pas&#8221; un moteur, elle &#8220;possède un moteur&#8221;. Certains se demandent peut être si une composition n&#8217;est pas plus indiquée dans ce cas et la réponse n&#8217;est pas évidente :</p>
<ul>
<li>En règle générale, oui, préférez la composition à l&#8217;héritage&#8230;</li>
<li>&#8230; mais dans le cas que nous présentons ici, la composition est assez &#8220;forte&#8221; : une voiture ne peut toujours avoir <strong>qu&#8217;un seul</strong> moteur, et le fait de démarrer la voiture <em>revient à</em> démarrer le moteur.</li>
</ul>
<p>Les cas où l&#8217;on peut légitimement accepter un héritage privé en lieu et place d&#8217;une composition restent très limités. Et personne ne vous blâmera si même dans ce cas, vous optez pour la composition. L&#8217;héritage privé est surtout une facilité du langage pour éviter au programmeur de saisir du code inutile : dans notre exemple, en utilisant une composition, on aurait obtenu quelque-chose de ce genre :</p><pre class="crayon-plain-tag">class Engine
{
  public:
    void start();
};

class Car
{
  public:
    void start() { m_engine.start(); }
  private:
    Engine m_engine;
};</pre><p>Pas vraiment plus difficile à comprendre, mais un peu plus long à taper. Et si ce n&#8217;était pas une mais vingt méthodes de <code>Engine</code> qu&#8217;il avait fallu &#8220;transporter&#8221; dans l&#8217;interface publique de <code>Car</code>, je vous laisse imaginer le temps perdu à saisir toutes les méthodes triviales qui ne font qu&#8217;appeler la &#8220;vraie&#8221; méthode du sous-objet. Dans tous les cas, notez qu&#8217;en t&#8217;en qu&#8217;utilisateur de la classe <code>Car</code>, vous ne <strong>devez jamais</strong> prendre en compte la présence d&#8217;un héritage privé dans sa définition : cet héritage est privé et <strong>relève de l&#8217;implémentation</strong> : l&#8217;auteur de la classe peut à tout moment décider de réécrire sa classe pour utiliser une composition classique, ou encore de redéfinir à la main chacune des méthodes, sans vous en notifier !</p>
<h2>Remarque</h2>
<p>Un lecteur assidu aura peut être remarqué une contradiction avec <a href="http://blog.freelan.org/2011/01/17/les-casts-en-cpp/">un de mes articles précédents</a> : en effet, si j&#8217;ai indiqué un peu plus haut qu&#8217;il n&#8217;était pas possible de convertir un <code>Car*</code> en <code>Engine*</code> en dehors des méthodes de <code>Car</code>, ce n&#8217;était pas tout à fait vrai : cela est possible en C++, par l’intermédiaire d&#8217;une &#8220;conversion à la façon C&#8221;. Évidemment, si on respecte la clause énoncée précédemment de ne <strong>jamais</strong> se baser sur le détail d&#8217;implémentation que représente l&#8217;<em>héritage privé</em>, il ne s&#8217;agit en fait que d&#8217;un argument supplémentaire contre l&#8217;utilisation de ce type de conversion. En un mot : faites-le une fois pour vous amuser, puis plus jamais !</p>
<h2>À propos du mot clé using</h2>
<p>S&#8217;il est très courant de rencontrer le mot clé <code>using</code> lors de l&#8217;utilisation d&#8217;héritage privé, il est également possible de l&#8217;utiliser lors d&#8217;un héritage &#8220;classique&#8221; public :</p><pre class="crayon-plain-tag">class Base
{
  public:
    int x() const { return m_x; }
  protected:
    void set_x(int value) { m_x = value; }
  private:
    int m_x;
};

class Derived : public Base
{
  public:
    using Base::set_x;
};</pre><p>Ici, nous avons <strong>augmenté la visibilité</strong> de la méthode <code>set_x()</code> en la rendant <strong>publique</strong> dans la classe dérivée, alors qu&#8217;elle n&#8217;était que <strong>protégée</strong> dans la classe de base. Bien que peu courante, cette syntaxe n&#8217;en demeure pas moins tout à fait correcte.</p>
<h1>L&#8217;héritage protégé</h1>
<p>L&#8217;héritage <strong>protégé</strong> ressemble énormément à l&#8217;héritage <strong>privé</strong>. En fait, sa seule différence est la suivante :</p>
<ul>
<li>Tout membre <strong>public</strong> ou <strong>protégé</strong> hérité au travers d&#8217;un <strong>héritage protégé</strong> est également accessible de façon <strong>protégée</strong> aux <strong>classes filles</strong> de la classe dérivée.</li>
</ul>
<p>Ce qui se traduit par :</p><pre class="crayon-plain-tag">class Engine
{
  protected:
    void start() {};
};

class Car : protected Engine {};

class Robot : protected Car
{
  public:
    using Engine::start;
};</pre><p>Ici <code>Robot</code> peut accéder à la méthode protégée de <code>Engine</code> parce que <code>Car</code> hérite de <code>Engine</code> au travers d&#8217;un <strong>héritage protégé</strong>. On voit que si l&#8217;héritage privé peut s&#8217;avérer utile dans certains cas, l&#8217;héritage protégé lui a un intérêt beaucoup plus limité, car le fait de pouvoir accéder aux méthodes de sa classe <em>grand-parente</em> ne fait qu&#8217;<strong>augmenter le couplage</strong>.</p>
<p>Je n&#8217;ai encore jamais rencontré ou eu besoin d&#8217;un héritage protégé depuis que je développe en C++.</p>
<h1>L&#8217;héritage virtuel</h1>
<p>Pour expliquer en quoi consiste l&#8217;héritage virtuel, replaçons avant tout dans le contexte quelques lieux communs :</p>
<h2>L&#8217;héritage multiple</h2>
<p>C++ est l&#8217;un des rares langages à autoriser l&#8217;héritage multiple là où les autres langages préfèrent imposer la notion d&#8217;interfaces. La légitimité de l&#8217;une ou de l&#8217;autre de ces techniques est un sujet à part entière et sort du cadre de cet article. Si vous vous intéressez à ce débat, il existe sur l&#8217;Internet <a href="http://www.artima.com/intv/abcs.html">bien des discussions</a> à ce sujet.</p>
<h3>La minute &#8220;rebelle&#8221;</h3>
<p>Vous avez certainement déjà entendu quelqu&#8217;un dire une chose du genre : &#8220;L&#8217;héritage multiple c&#8217;est toujours une hérésie, ça n&#8217;aurait jamais du exister ! Je n&#8217;ai jamais réussi à en faire quoi que ce soit d&#8217;utile.&#8221;</p>
<p>Face à ce genre de remarque simpliste, j&#8217;ai tendence à avoir la même réaction que <a title="www.parashift.com/" href="http://www.parashift.com/" target="_blank">Marshall Cline</a> : les gens qui vous disent ça ne <strong>connaissent</strong> à priori <strong>pas votre problème</strong> ou vos besoins, et pourtant ils prétendent y répondre : comment le pourraient-ils ? Comment peuvent-ils savoir que dans <strong>votre situation</strong>, l&#8217;héritage multiple n&#8217;est pas la meilleure solution ? Si vous rencontrez ce genre de personnes, soyez prudents : le manque d&#8217;ouverture d&#8217;esprit et de réflexion fait en général de bien piètres programmeurs. Une phrase que j&#8217;aime bien citer dans cette situation est celle-ci : &#8220;Toutes les phrases qui dénoncent un principe de façon absolue et générale sont absolument et généralement fausses&#8221;. Si l&#8217;héritage multiple semble être la solution à votre problème, n&#8217;hésitez pas à l&#8217;utiliser.</p>
<p>Dans tous les cas, réfléchissez toujours bien à ce que vous faites : si l&#8217;héritage multiple est tant victime d&#8217;à priori négatifs, c&#8217;est qu&#8217;il est parfois difficile de comprendre son utilisation.</p>
<h2>La démonstration par l&#8217;exemple</h2>
<p>Pour illustrer les notions d&#8217;héritage multiple et virtuel, créons tout d&#8217;abord une hiérarchie de classes :</p>
<div id="attachment_341" style="width: 434px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/03/shapes.png"><img class="size-full wp-image-341" title="Hiérarchie de classe à héritage multiple" src="http://blog.freelan.org/wp-content/uploads/2011/03/shapes.png" alt="" width="424" height="365" /></a><p class="wp-caption-text">Hiérarchie de classe à héritage multiple</p></div>
<p>Comme nous le voyons ici, nous partons d&#8217;une classe <code>Shape</code> (&#8220;forme&#8221; en anglais), de laquelle dérivent deux classes <code>Rectangle</code> et <code>Diamond</code> (&#8220;losange&#8221; en anglais). Une quatrième classe, <code>Square</code> (&#8220;carré&#8221; en anglais) hérite à la fois de <code>Rectangle</code> et de <code>Diamond</code>.</p>
<p>Une implémentation naïve de cette hiérarchie en C++ pourrait ressembler à :</p><pre class="crayon-plain-tag">class Shape
{
  public:
    int identifier() const { return m_identifier; }
    virtual void draw() = 0;
  private:
    int m_identifier;
};

class Rectangle : public Shape
{
  public:
    void draw() { /* ... */ }
};

class Diamond : public Shape
{
  public:
    void draw() { /* ... */ }
};

class Square : public Rectangle, public Diamond
{
  public:
    void draw() { /* ... */ }
};</pre><p>Cela a du sens : un carré (si on s&#8217;en réfère aux lois de la géométrie) est à la fois un rectangle et un losange, et il est aussi une forme.</p>
<p>Cependant, ce code n&#8217;a en pratique pas la structure souhaitée :</p>
<p>En effet, en utilisant un héritage classique (non virtuel) <code>Square</code> hérite en pratique deux fois de <code>Shape</code> : une fois par la branche de gauche (<code>Rectangle</code>), et une fois par la branche de droite (<code>Diamond</code>). Il en résulte que chaque instance de <code>Square</code> possède deux instances de <code>m_identifier</code>. Lorsqu&#8217;on souhaite utiliser <code>identifier()</code> ou <code>m_identifier</code> dans ou en dehors de la classe <code>Square</code>, il faut préciser par quelle branche on passe :</p>
<ul>
<li>Soit en préfixant m_identifier par <code>Diamond</code> ou <code>Rectangle</code> (&#8220;<code>Rectangle::m_identifier</code>&#8220;);</li>
<li>soit en effectuant auparavant une conversion de <code>this</code> vers <code>Rectangle*</code> ou <code>Diamond*</code> (&#8220;<code>static_cast<Rectangle*>(this)->m_identifier</code>&#8220;)</li>
</ul>
<p>Ce n&#8217;est généralement pas ce qui est souhaité.</p>
<p>Pour résoudre ce problème, nous avons besoin de l&#8217;<strong>héritage virtuel</strong> :</p><pre class="crayon-plain-tag">class Shape
{
  public:
    int identifier() const { return m_identifier; }
    virtual void draw() = 0;
  private:
    int m_identifier;
};

class Rectangle : public virtual Shape // Notez la pr&eacute;sence de &quot;virtual&quot; ici
{
  public:
    void draw() { /* ... */ }
};

class Diamond : public virtual Shape // Notez la pr&eacute;sence de &quot;virtual&quot; ici
{
  public:
    void draw() { /* ... */ }
};

class Square : public Rectangle, public Diamond
{
  public:
    void draw() { /* ... */ }
};</pre><p>En spécifiant que <code>Rectangle</code> et <code>Diamond</code> héritent tous deux virtuellement de <code>Shape</code>, nous empêchons la <em>multiple instanciation</em> du type de base : il n&#8217;y a alors plus qu&#8217;une seule instance de <code>m_identifier</code> et on peut y référer directement sans avoir recours à des conversions.</p>
<h2>Bonnes pratiques</h2>
<p>L&#8217;utilisation de l&#8217;héritage virtuel peut se faire conjointement avec tout autre type d&#8217;héritage (privé, protégé et public), mais est habituellement rencontré principalement avec l&#8217;<strong>héritage public</strong>.</p>
<p>Indépendamment de l&#8217;utilisation de l&#8217;héritage virtuel, on constate assez logiquement que les classes les plus en haut de la hiérarchie devraient dans l&#8217;idéal être virtuelles pures. Si c&#8217;est le cas, cela ne signifie <strong>pas forcément</strong> qu&#8217;il faille supprimer l&#8217;héritage virtuel : outre le fait de ne pas dupliquer inutilement les instances des membres, l&#8217;héritage virtuel permet également de s&#8217;assurer que la classe de base n&#8217;a qu&#8217;<strong>une seule adresse</strong> dans les instances filles. Il est en pratique très probable que vous deviez user d&#8217;héritage virtuel lorsque vous utilisez l&#8217;héritage multiple.</p>
<h1>Conclusion</h1>
<p>Nous avons vu au travers des différentes sections que C++ est très complet en matière d&#8217;héritage. Élément incontournable de la programmation orientée objet moderne, c&#8217;est un principe qu&#8217;il convient de manier avec la plus grande précaution et une bonne réflexion : si l&#8217;héritage peut résoudre bien des problèmes, il n&#8217;est de loin pas la solution universelle. Que vous optiez pour l&#8217;héritage, la composition ou autre chose pour la résolution de vos problèmes, réfléchissez toujours et <strong>envisagez chacune des possibilités</strong>.</p>
<p>Comme toujours, n&#8217;hésitez pas à me faire part de vos remarques, questions ou corrections dans les commentaires.</p>
<h1>Sources</h1>
<p>Voici une série de liens en anglais qui m&#8217;ont inspiré pour la rédaction de cet article. N&#8217;hésitez pas à les consulter, ils sont extrêmement intéressants :</p>
<ul>
<li><a href="http://www.parashift.com/c++-faq-lite/private-inheritance.html">L&#8217;héritage privé/protégé sur parashift</a></li>
<li><a href="http://stackoverflow.com/questions/4605556/when-virtual-inheritance-is-a-good-design">StackOverflow</a></li>
<li><a href="http://en.allexperts.com/q/C-1040/virtual-inheritance.htm">L&#8217;héritage virtuel sur allexperts</a></li>
<li><a href="http://www.parashift.com/c++-faq-lite/multiple-inheritance.html">L&#8217;héritage virtuel sur parashift</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/03/08/lheritage-en-c/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Découverte de Git sous Windows</title>
		<link>https://blog.freelan.org/2011/02/02/decouverte-de-git-sous-windows/</link>
		<comments>https://blog.freelan.org/2011/02/02/decouverte-de-git-sous-windows/#comments</comments>
		<pubDate>Wed, 02 Feb 2011 17:41:36 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Logiciels]]></category>
		<category><![CDATA[développement]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=222</guid>
		<description><![CDATA[J'ai commencé à entendre parler de Git il y a quelques temps déjà mais je n'avais jamais eu l'occasion de vraiment travailler avec. Pour moi, son seul intérêt était qu'il était distribué, mais sans comprendre exactement ce que ça impliquait.

Aujourd'hui les choses ont changé et j'ai, pour mon plus grand bonheur découvert un outil incroyablement puissant et efficace. Git n'est pas simplement un autre gestionnaire de sources, c'est une nouvelle façon de penser le développement.

Dans cet article, nous allons présenter Git en comparaison avec d'autres gestionnaires de sources plus "classiques" puis expliquer son installation sous Windows.]]></description>
				<content:encoded><![CDATA[<p>J&#8217;ai commencé à entendre parler de Git il y a quelques temps déjà mais je n&#8217;avais jamais eu l&#8217;occasion de vraiment travailler avec. Pour moi, son seul intérêt était qu&#8217;il était distribué, mais sans comprendre exactement ce que ça impliquait.  Aujourd&#8217;hui les choses ont changé et j&#8217;ai, pour mon plus grand bonheur découvert un outil incroyablement puissant et efficace. À mon sens, git n&#8217;est pas simplement un autre gestionnaire de sources, c&#8217;est une nouvelle façon de penser le développement.  Dans cet article, nous allons <strong>brièvement</strong> présenter Git en comparaison avec d&#8217;autres gestionnaires de sources plus &#8220;classiques&#8221; puis expliquer son <strong>installation sous Windows</strong>.</p>
<h1>Présentation</h1>
<p>Il existe de très nombreuses ressources sur l&#8217;Internet (mais pas que) qui présentent <a href="http://git-scm.com/">Git</a> de façon très complète. L&#8217;objectif de cet article n&#8217;est pas de faire de vous un expert Git en quelques pages mais simplement de vous présenter l&#8217;outil et de, j&#8217;espère, vous donner l&#8217;envie de l&#8217;utiliser.  si vous connaissez déjà suffisamment Git, vous pouvez sauter la section suivante et directement vous rendre à la partie &#8220;Installation sous Windows&#8221;.</p>
<h2>Un gestionnaire de sources</h2>
<p>Git est un <strong>gestionnaire de sources</strong>, comme <a href="http://subversion.apache.org/">Subversion (SVN)</a>, <a href="http://cvs.nongnu.org/">CVS</a> ou encore <a href="http://msdn.microsoft.com/fr-fr/library/3h0544kx(v=vs.80).aspx">Visual Source Safe (VSS)</a>. Un outil qui assure principalement les rôles suivants :</p>
<ul>
<li>Il permet de stocker différentes versions du code source.</li>
<li>Il permet un travail collaboratif en facilitant la synchronisation entre différents développeurs.</li>
<li>Il permet de savoir qui est à l&#8217;origine d&#8217;une modification</li>
</ul>
<p>Les trois derniers outils que j&#8217;ai cités ont en commun leur <strong>architecture centralisée</strong> : tous les développeurs synchronisent leurs fichiers de code source auprès d&#8217;un <strong>dépôt central</strong>. Cette approche, très simple à comprendre est souvent plébiscitée par les entreprises, pour différentes raisons :</p>
<ul>
<li>L&#8217;administration d&#8217;un dépôt central est plutôt simple : on peut régler finement et individuellement les droits d&#8217;accès.</li>
<li>Tout le monde comprend la notion de &#8220;publier&#8221; ses sources dans un dépôt central.</li>
<li>En entreprise, en général, les liaisons réseaux sont fiables et robustes; l&#8217;accès 24H/24 au dépôt est quasi-garanti.</li>
<li>Les clients ne disposent que du code source sur lequel ils travaillent actuellement; l&#8217;historique reste, lui, dans le dépôt.</li>
</ul>
<p>Git se distingue principalement par son <strong>architecture distribuée</strong>. Avec Git, il n&#8217;y a pas de dépôt central, principal, ou autre : chaque dépôt Git contient tout l&#8217;historique du dépôt, de façon autonome. Il n&#8217;y a pas d&#8217;entité supérieure. Les dépôts peuvent communiquer entre eux selon des règles très simples.  Ses avantages principaux sont les suivants :</p>
<ul>
<li>Le dépôt, puisque local et autonome, est <strong>toujours accessible</strong> et extrêmement rapide, même lorsque déconnecté du réseau.</li>
<li>Git ne stocke pas les fichiers, mais <strong>les différences</strong> entre les fichiers : ce principe lui permet de disposer d&#8217;un des mécanismes de <strong>fusion</strong> (ou &#8220;merge&#8221;) les plus efficaces.</li>
<li>Contrôle total sur le dépôt : on ne dépend plus d&#8217;une entité externe. Avec une solution centralisée, si le dépôt nous lâche, adieu l&#8217;historique.</li>
<li>Un système de branchage extrêmement efficace.</li>
</ul>
<p><em>Note : Il n&#8217;y <strong>aucun mal</strong> à utiliser un gestionnaire de sources centralisé; c&#8217;est d&#8217;ailleurs dans bien des cas la solution la plus adaptée. Cependant, puisque cet article parle essentiellement de Git, nous allons nous concentrer sur des cas où l&#8217;utilisation de Git semble plus opportune.</em> Vous trouverez <a href="http://en.wikipedia.org/wiki/Git_(software)">ici</a> un historique plus précis de git et des raisons de sa création.</p>
<h2>Avant de commencer</h2>
<p>Si vous êtes déjà habité à utiliser un gestionnaire de sources &#8220;classique&#8221;, avant de continuer prenez quelques minutes pour vous rappeler de leur fonctionnement puis <strong>oubliez tout !</strong> En tout cas temporairement : l&#8217;utilisation de Git est drastiquement différente et lui appliquer la logique d&#8217;un gestionnaire de source centralisé n&#8217;aurait <strong>aucun sens</strong>.  Pour ma part, je suis habitué à utiliser SVN et il m&#8217;a fallu une certaine période d&#8217;adaptation pour comprendre que son fonctionnement était plus différent qu&#8217;il n&#8217;y paraissait.</p>
<p><em>Note : Bien que différents, il est tout à fait possible d&#8217;utiliser un dépôt SVN avec la ligne de commande git, grace à &#8220;git svn&#8221; qui s&#8217;avère être un moyen très simple de découvrir git sans changer ses dépôts existants. Je l&#8217;utilise depuis plusieurs semaines maintenant et j&#8217;en suis vraiment très satisfait.</em></p>
<h2>Concepts</h2>
<p>Chaque dépôt Git conserve un historique de tous les <strong>changements</strong> (ou &#8220;commits&#8221;) depuis sa création, potentiellement au sein de plusieurs <strong>branches</strong>.  Au sein d&#8217;un dépôt, on ne peut travailler <strong>à la fois</strong> que dans une seule branche et sur un seul commit. Lorsqu&#8217;on a effectué des changements au sein d&#8217;une branche, on peut librement les enregistrer à la suite de la branche (on parle de faire un &#8220;commit&#8221;) et continuer (éventuellement en allant travailler dans une autre branche).  Il est par la suite possible de fusionner (faire un &#8220;merge&#8221;) de plusieurs branches pour répercuter les changements de l&#8217;une dans l&#8217;autre.  La philosophie de Git, c&#8217;est de faire des commits très souvent, même (et surtout) pour de petites modifications. Plus vous &#8220;commiterez&#8221; souvent, plus il sera facile pour Git de fusionner vos changements avec ceux des autres.</p>
<h1>Installation sous Windows</h1>
<p>J&#8217;ai longtemps travaillé avec Subversion (SVN) sous Windows en utilisant <a href="http://tortoisesvn.tigris.org/">TortoiseSVN</a>. Cet outil graphique est plutôt bien pensé et m&#8217;a toujours satisfait. En voulant essayer git, je me suis donc naturellement porté vers <a href="http://code.google.com/p/tortoisegit/">TortoiseGIT</a> mais je dois avouer que j&#8217;ai été déçu. Je pense que les gens derrière TortoiseGIT ont fait un travail remarquable et ils continuent d&#8217;améliorer le logiciel, mais à l&#8217;heure actuelle l&#8217;outil me paraît plus contraignant à utiliser que sa version classique en <strong>ligne de commande</strong> (comme sous Linux).  C&#8217;est donc sur cette dernière que va porter l&#8217;installation.</p>
<h2>Téléchargements</h2>
<p>Commencez par télécharger git en vous rendant sur <a href="http://code.google.com/p/msysgit/downloads/list">cette page</a>. Prenez la dernière version existante de git (Version &#8220;<a href="http://code.google.com/p/msysgit/downloads/detail?name=Git-1.7.3.1-preview20101002.exe&amp;can=2&amp;q=">Git-1.7.3.1-preview20101002.exe</a>&#8221; à l&#8217;heure actuelle).  Exécutez l&#8217;installation :</p>
<div id="attachment_226" style="width: 523px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-install.png"><img class="size-full wp-image-226" title="Installation de git" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-install.png" alt="Installation de git" width="513" height="398" /></a><p class="wp-caption-text">Installation de git</p></div>
<p>Choisissez les modules que vous souhaitez installer. Pour ma part, j&#8217;ai choisi ces modules :</p>
<div id="attachment_227" style="width: 523px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-options.png"><img class="size-full wp-image-227" title="Modules à installer" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-options.png" alt="Modules à installer" width="513" height="398" /></a><p class="wp-caption-text">Modules à installer</p></div>
<p>Enfin, l&#8217;installeur vous demande de quelle façon vous souhaitez installer git. Les deux premières options nécessitent l&#8217;utilisation d&#8217;une console de type UNIX (msys/cygwin) pour l&#8217;utilisation de Git, la dernière permet d&#8217;utiliser git depuis une console native (type &#8220;cmd.exe&#8221; ou encore Powershell).</p>
<div id="attachment_228" style="width: 523px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-mode.png"><img class="size-full wp-image-228" title="Mode d'installation de git" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-mode.png" alt="Mode d'installation de git" width="513" height="398" /></a><p class="wp-caption-text">Mode d&#39;installation de git</p></div>
<p>C&#8217;est cette dernière option que nous choisirons pour les raisons suivantes :</p>
<ul>
<li>La console UNIX est vraiment très puissante et efficace&#8230; mais, à mon sens, assez peu adaptée à Windows.</li>
<li>De nombreux développeurs Windows utilisent déjà Powershell et donc il faut pouvoir utiliser git depuis n&#8217;importe quelle console. Changer de console juste pour commit ses changements dans git n&#8217;est pas envisageable.</li>
</ul>
<p>Notez que comme il est indiqué dans l&#8217;installeur, la dernière option va remplacer une partie des outils Windows (tels que find.exe et sort.exe) par leur équivalent GNU. Si vous utilisez ces outils, vous pourrez toujours les utiliser, mais il faudra les préfixer par leur chemin complet.  Une fois l&#8217;installation terminée, lancez une console puis saisissez la commande :</p><pre class="crayon-plain-tag">git --version</pre><p>Si vous obtenez la sortie suivante :</p>
<div id="attachment_229" style="width: 687px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-mode1.png"><img class="size-full wp-image-229" title="Version de git" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-mode1.png" alt="Obtenir la version installée de git" width="677" height="250" /></a><p class="wp-caption-text">Obtenir la version installée de git</p></div>
<p>C&#8217;est que git est correctement installé et fonctionnel. Félicitations !</p>
<h1>Configuration préliminaire</h1>
<p>Il est possible de configurer un bon nombre de choses dans git. Cela passe du nom d&#8217;utilisateur au proxy à utiliser ou à l&#8217;activation des couleurs lors de l&#8217;affichage des informations du dépôt.  Les configurations de git sont soit <strong>globales</strong>, soit propres à chaque dépôt. En pratique, si une valeur de paramètre n&#8217;est pas trouvée au sein du dépôt, git va regarder la configuration globale.  Voici les paramètres les plus couramment modifiés :</p><pre class="crayon-plain-tag">git config --global user.name &quot;Votre nom&quot;
git config --global user.email &quot;adresse@email.com&quot;
git config --global color.branch auto
git config --global color.diff auto
git config --global color.interactive auto
git config --global color.status auto</pre><p>Le deux premiers sont assez explicites; les quatre suivant activent les couleurs lors des commandes associées (ce qui est quand même plus sympathique).  Il existe évidemment encore bien d&#8217;autres paramètres, je vous invite à consulter la <a href="http://www.kernel.org/pub/software/scm/git/docs/git-config.html">page de manuel de git-config</a> pour les découvrir.</p>
<h1>Utilisation basique</h1>
<p>Le moyen le plus efficace, c&#8217;est de pratiquer. Commençons par créer un dépôt git :</p>
<div id="attachment_231" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-init.png"><img class="size-full wp-image-231" title="Création d'un dépôt git" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-init.png" alt="Création d'un dépôt git" width="837" height="218" /></a><p class="wp-caption-text">Création d&#39;un dépôt git</p></div>
<p><em>Note : Plutôt que de créer un dépôt vide, on aurait également pu choisir de <strong>cloner</strong> un dépôt existant avec la commande &#8220;git clone&#8221;.</em></p>
<p><em> </em>Créons un fichier à ajouter au dépôt :</p>
<div id="attachment_233" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-status.png"><img class="size-full wp-image-233" title="Création d'un fichier à ajouter au dépôt" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-status.png" alt="Création d'un fichier à ajouter au dépôt" width="837" height="330" /></a><p class="wp-caption-text">Création d&#39;un fichier à ajouter au dépôt</p></div>
<p>La commande &#8220;git status&#8221; permet d&#8217;interroger le dépôt sur le statut actuel de la <strong>copie de travail</strong> (ou &#8220;working copy&#8221;).  Ici, on voit que le fichier main.cpp a été créé mais n&#8217;est pas <strong>suivi</strong> (ou &#8220;tracked&#8221;) par le dépôt.  Indiquons à git que ce fichier doit être suivi et faire partie du prochain commit :</p>
<div id="attachment_234" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-add.png"><img class="size-full wp-image-234" title="Ajout d'un fichier au dépôt" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-add.png" alt="Ajout d'un fichier au dépôt" width="837" height="330" /></a><p class="wp-caption-text">Ajout d&#39;un fichier au dépôt</p></div>
<p>Pour ce faire, nous avons utilisé la commande &#8220;git add&#8221; qui permet d&#8217;indiquer qu&#8217;un fichier (même si il est déjà suivi) doit faire partie du prochain commit.</p>
<p>Après &#8220;git add&#8221;, &#8220;git status&#8221; nous indique bel est bien que le fichier doit être suivi.  Nous pourrions à ce moment là, ajouter d&#8217;autres fichiers, en modifier, voire en supprimer ou en déplacer certains. Il faut simplement signaler à git que le changement (quel que soit sa nature) doit faire partie du prochain commit.</p>
<p>Nous nous contentons de ce simple ajout pour l&#8217;instant.</p>
<div id="attachment_236" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-commit.png"><img class="size-full wp-image-236" title="Historisation d'un changement" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-commit.png" alt="Historisation d'un changement" width="837" height="330" /></a><p class="wp-caption-text">Historisation d&#39;un changement</p></div>
<p>La commande &#8220;git commit&#8221; permet de sauver le changement au sein du dépôt. Ici le paramètre &#8220;-m&#8221; permet de spécifier un message à associer au commit. Sans ça, git aurait ouvert un éditeur de texte pour nous demander de saisir notre message.</p>
<p><em>Note : La saisie d&#8217;un message est extrêmement importante. Les messages ne doivent pas forcément être très longs, mais il ne devraient jamais être vides. À quoi sert-il de mettre ses changements sous historiques si on est incapable de dire pourquoi ils ont été faits ? <strong>Même lorsque vous travaillez seul</strong>, prenez l&#8217;habitude de toujours renseigner des messages informatifs pour chacun de vos commits. C&#8217;est une habitude que <strong>vous ne regrettez pas</strong>.</em></p>
<p><em> </em>Après le commit, on constate que la &#8220;working copy&#8221; a été mise à zéro : &#8220;git status&#8221; indique qu&#8217;aucun changement n&#8217;a été apporté depuis le commit actuel.  Nous pouvons bien entendu dès à présent consulter l&#8217;historique de notre dépôt grâce à la commande &#8220;git log&#8221; :</p>
<div id="attachment_237" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-log.png"><img class="size-full wp-image-237" title="Consultation de l'historique" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-log.png" alt="Consultation de l'historique" width="837" height="442" /></a><p class="wp-caption-text">Consultation de l&#39;historique</p></div>
<p>Le commit est bien dans l&#8217;historique. Il s&#8217;est vu assigner un identifiant unique &#8220;c0d281e3532a4970415bba1e9159b1dc7ed816b1&#8243;.  Modifions maintenant le fichier main.cpp, de la façon suivante :</p>
<div id="attachment_238" style="width: 830px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/main.cpp_.png"><img class="size-full wp-image-238" title="Le fichier main.cpp modifié" src="http://blog.freelan.org/wp-content/uploads/2011/02/main.cpp_.png" alt="Le fichier main.cpp modifié" width="820" height="442" /></a><p class="wp-caption-text">Le fichier main.cpp modifié</p></div>
<p>Après sauvegarde des modifications, on utilise &#8220;git status&#8221; qui nous informe qu&#8217;en effet, main.cpp a été modifié par rapport à la version actuelle :</p>
<div id="attachment_239" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-status1.png"><img class="size-full wp-image-239" title="Le fichier main.cpp a été modifié" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-status1.png" alt="Le fichier main.cpp a été modifié" width="837" height="442" /></a><p class="wp-caption-text">Le fichier main.cpp a été modifié</p></div>
<p>Notons que bien que modifié, le fichier n&#8217;est pas automatiquement marqué comme faisant partie du prochain commit.</p>
<p>Nous pouvons afficher la liste des modifications apportées au fichier grâce à la commande &#8220;git diff&#8221; :</p>
<div id="attachment_240" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-diff.png"><img class="size-full wp-image-240" title="Utilisation de git diff pour lister les changements" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-diff.png" alt="Utilisation de git diff pour lister les changements" width="837" height="714" /></a><p class="wp-caption-text">Utilisation de git diff pour lister les changements</p></div>
<p>Nous souhaitons archiver cette nouvelle version de main.cpp. Pour ce faire, nous pouvons faire &#8220;git add main.cpp&#8221; suivi de &#8220;git commit&#8221; ou bien directement :</p>
<div id="attachment_241" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-commit1.png"><img class="size-full wp-image-241" title="Archivage de la correction de main.cpp" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-commit1.png" alt="Archivage de la correction de main.cpp" width="837" height="202" /></a><p class="wp-caption-text">Archivage de la correction de main.cpp</p></div>
<p>L&#8217;argument &#8220;-a&#8221; permet de dire à &#8220;git commit&#8221; qu&#8217;il doit automatiquement ajouter tous les fichiers <strong>déjà suivis</strong> qui ont été <strong>modifiés</strong> depuis le dernier commit.</p>
<p>Encore une fois, un appel &#8220;git log&#8221; nous donne la sortie suivante :</p>
<div id="attachment_242" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-log1.png"><img class="size-full wp-image-242" title="Affichage des logs du dépôt" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-log1.png" alt="Affichage des logs du dépôt" width="837" height="778" /></a><p class="wp-caption-text">Affichage des logs du dépôt</p></div>
<p>Le changement a bien été archivé.  Il est possible de revenir à un état antérieur du dépôt grâce à la commande &#8220;git checkout&#8221; :</p>
<div id="attachment_243" style="width: 847px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2011/02/git-checkout.png"><img class="size-full wp-image-243" title="Utilisation de git checkout" src="http://blog.freelan.org/wp-content/uploads/2011/02/git-checkout.png" alt="Utilisation de git checkout" width="837" height="362" /></a><p class="wp-caption-text">Utilisation de git checkout</p></div>
<p>Et bien entendu de revenir à la dernière version en date en utilisant : &#8220;git checkout master&#8221;, où &#8220;master&#8221; est le nom de la branche à utiliser.  <em>Note : par défaut, la branche principale d&#8217;un dépôt git est nommée &#8220;master&#8221;. Il s&#8217;agit d&#8217;une convention, que vous êtes libre de ne pas suivre, mais qu&#8217;il est tout de même recommandé de respecter.</em></p>
<h1>Aller plus loin</h1>
<p>Nous n&#8217;avons vu ici qu&#8217;un petit aperçu de l&#8217;utilisation et du principe de git : il y a bien plus à voir et à découvrir.  Il est également possible de (liste loin d&#8217;être exhaustive) :</p>
<ul>
<li>Partager ses modifications avec un ou plusieurs autres dépôts (&#8220;push&#8221;, &#8220;pull&#8221;, &#8220;fetch&#8221;, etc.).</li>
<li>Effacer localement certains commit intermédiaires ou de réécrire totalement l&#8217;historique (lorsque cela est absolument nécessaire).</li>
<li>Utiliser git avec un dépôt SVN (&#8220;git svn&#8221;) pour par exemple faciliter la transition.</li>
<li>Rechercher quelle modification a introduit un bogue (&#8220;git bissect&#8221;)</li>
</ul>
<p>Pour tout découvrir, je vous recommande notamment le livre <a href="http://www.pragprog.com/titles/pg_git/pragmatic-guide-to-git">Pragmatic Guide to Git</a> qui est à la fois très facile d&#8217;accès et très complet. N&#8217;hésitez pas non plus à consulter les pages de manuel de git qui sont très bien fournies.  Comme toujours, n&#8217;hésitez pas à poser vos questions si j&#8217;ai manqué de clarté sur certains aspects.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/02/02/decouverte-de-git-sous-windows/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Les casts en C++</title>
		<link>https://blog.freelan.org/2011/01/17/les-casts-en-cpp/</link>
		<comments>https://blog.freelan.org/2011/01/17/les-casts-en-cpp/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 17:30:05 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[cast]]></category>
		<category><![CDATA[const_cast]]></category>
		<category><![CDATA[conversion]]></category>
		<category><![CDATA[conversions]]></category>
		<category><![CDATA[dynamic_cast]]></category>
		<category><![CDATA[reinterpret_cast]]></category>
		<category><![CDATA[static_cast]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=196</guid>
		<description><![CDATA[Nombreux sont les programmeurs C++ qui ont d'abord été confrontés au C. Les deux langages partagent en effet bien des fonctionnalités... mais ont également de grandes différences.

Parmi ces différences, on trouve les opérateurs de conversion C++. Ils sont certainement l'un des points les plus mal compris par les développeurs C qui voient souvent en eux un verbiage inutile. L'objectif de cet article est de (dé)montrer l'utilité des opérateurs de conversion C++, en comparaison avec les conversions classiques, dites : "à la C" et de comprendre ce qu'ils peuvent apporter au programmeur en termes de maintenabilité et de sécurité.]]></description>
				<content:encoded><![CDATA[<p>Nombreux sont les programmeurs C++ qui ont d&#8217;abord été confrontés au C. Les deux langages partagent en effet bien des fonctionnalités&#8230; mais ont également de grandes différences.</p>
<p>Parmi ces différences, on trouve les opérateurs de conversion C++. Ils sont certainement l&#8217;un des points les plus mal compris par les développeurs C qui voient souvent en eux un verbiage inutile. L&#8217;objectif de cet article est de (dé)montrer l&#8217;utilité des opérateurs de conversion C++, en comparaison avec les conversions classiques, dites : &#8220;à la C&#8221; et de comprendre ce qu&#8217;ils peuvent apporter au programmeur en termes de maintenabilité et de sécurité.</p>
<h1>Un petit mot sur les conversions</h1>
<p>Les conversions (ou &#8220;cast&#8221; en anglais) sont un des outils incontournables du programmeur C++. Mais comme tout outil, il faut savoir les utiliser à bon escient.</p>
<p>Dans l&#8217;idéal, un programme doit contenir le moins possible de &#8220;casts&#8221; : les types doivent s&#8217;interfacer naturellement les uns avec les autres. Cela garantit un <strong>découplage</strong> du code et donc une meilleure maintenabilité. Cela ne signifie pas qu&#8217;il faille à tout prix éviter les &#8220;casts&#8221; mais simplement qu&#8217;il faut les utiliser avec parcimonie.</p>
<p>Dans les sections qui suivent, nous allons expliquer le rôle de chaque opérateur de conversion. Pour l&#8217;ensemble des sections, nous considérerons les classes suivantes lorsqu&#8217;il sera question de hiérarchie :</p><pre class="crayon-plain-tag">class Base { public: virtual ~Base() {} };
class Derived : public Base {};
class Derived2 : public Base {};</pre><p></p>
<h1>static_cast&lt;&gt;</h1>
<p>Il permet plusieurs choses :</p>
<ul>
<li>Expliciter les conversions implicites, supprimant du même fait <strong>tout avertissement</strong> que donnerait le compilateur si la conversion peut entraîner un risque. Exemple : <strong>double</strong> vers <strong>int</strong>.</li>
<li>Convertir vers et depuis n&#8217;importe quel type pointé à partir d&#8217;un <strong>void*</strong>. Exemple : <strong>void*</strong> vers <strong>unsigned char*</strong>.</li>
<li>Convertir au travers d&#8217;une hiérarchie de classe, <strong>sans effectuer de vérification préalable</strong>. Exemple : <strong>Base*</strong> vers <strong>Derived*</strong> ou <strong>Base&amp;</strong> vers <strong>Derived&amp;</strong>.</li>
<li>Ajouter l&#8217;attribut constant au type converti. Exemple : <strong>char*</strong> vers <strong>const char*</strong>.</li>
</ul>
<p>Dans le dernier cas, notez que puisqu&#8217;il n&#8217;y a <strong>aucune vérification</strong> et que <strong>static_cast&lt;&gt;</strong> n&#8217;échoue <strong>jamais</strong>, le code suivant a un <strong>comportement indéfini</strong> (communément nommé en anglais &#8220;<em>undefined behavior</em>&#8221; ou &#8220;<em>UB</em>&#8220;) :</p><pre class="crayon-plain-tag">void foo()
{
Base* base = new Derived();

// Ce code est valide et correct car base pointe en fait vers une instance de Derived.
Derived* derived = static_cast&amp;lt;Derived*&amp;gt;(base);

// Ce code compile mais &agrave; un comportement ind&eacute;fini (Undefined Behavior)
// car base ne pointe pas vers une instance de Derived2.
Derived2* derived2 = static_cast&amp;lt;Derived2*&amp;gt;(base);
}</pre><p>Notez que la notion de <strong>comportement indéfini</strong> n&#8217;offre par définition <strong>aucune garantie</strong> : le code peut avoir le comportement espéré, faire crasher le programme ou provoquer l&#8217;envoi d&#8217;un missile nucléaire sur Cuba.</p>
<p>Il ne permet <strong>pas</strong> de :</p>
<ul>
<li>Convertir vers ou depuis un type pointé à partir d&#8217;un autre type pointé <strong>autre que void*</strong>. Exemple : <strong>unsigned char*</strong> vers <strong>char*</strong>.</li>
<li>Tester qu&#8217;une instance est celle d&#8217;un type dérivé. Exemple : tester qu&#8217;un <strong>Base*</strong> est en fait un <strong>Derived*</strong>.</li>
<li>Supprimer l&#8217;attribut <strong>constant</strong> du type converti. Exemple : <strong>const char*</strong> vers <strong>char*</strong>.</li>
</ul>
<h2>En bref</h2>
<p><strong>static_cast&lt;&gt;</strong> est sans doute l&#8217;opérateur de conversion que vous serez amené à utiliser le plus. Il ne permet que de réaliser des conversions sûres et à pour rôle principal celui d&#8217;expliciter les conversions implicites.</p>
<p>Dans le cas du polymorphisme, il est à préférer à <strong>dynamic_cast&lt;&gt;</strong> lorsque l&#8217;on a <strong>la garantie</strong> que la conversion va réussir.</p>
<h1>dynamic_cast&lt;&gt;</h1>
<p>Le seul rôle de <strong>dynamic_cast&lt;&gt;</strong> est de tester à l&#8217;exécution si un pointeur d&#8217;un type de base est en fait un pointeur vers un type dérivé.</p>
<p>Exemple :</p><pre class="crayon-plain-tag">void foo()
{
Base* base = new Derived();

// Apr&egrave;s l'appel, derived pointe vers l'instance de Derived
// car base pointe en fait vers une instance de Derived.
Derived* derived = dynamic_cast&amp;lt;Derived*&amp;gt;(base);

// Apr&egrave;s l'appel, derived2 vaut 0
// car base ne pointe pas vers une instance de Derived2.
Derived2* derived2 = dynamic_cast&amp;lt;Derived2*&amp;gt;(base);

// Dans le cas d'une r&eacute;f&eacute;rence, si le cast n'est pas possible
// Une exception de type std::bad_alloc est lanc&eacute;e.
Derived2&amp;amp; derived2_bis = dynamic_cast&amp;lt;Derived2&amp;amp;&amp;gt;(*base);
}</pre><p>Note : pour que <strong>dynamic_cast&lt;&gt;</strong> fonctionne, le type de base doit posséder <strong>au moins une</strong> méthode virtuelle.</p>
<p>Un appel à <strong>dynamic_cast&lt;&gt;</strong> est plus coûteux qu&#8217;un appel à <strong>static_cast&lt;&gt;</strong>car <strong>dynamic_cast&lt;&gt;</strong> effectue une recherche dans la &#8220;<em>v-table</em>&#8221; de l&#8217;instance <strong>à l&#8217;exécution</strong> pour déterminer son type exact.</p>
<p>On veillera donc à n&#8217;utiliser <strong>dynamic_cast&lt;&gt;</strong> que lorsqu&#8217;il n&#8217;y a aucune autre solution.</p>
<h2>En bref</h2>
<p><strong>dynamic_cast&lt;&gt;</strong> est le seul opérateur de conversion à avoir un effet &#8220;indéterminé&#8221; jusqu&#8217;à l&#8217;exécution. Son utilisation n&#8217;a de sens que lorsque confronté à du polymorphisme. Dans les cas où la conversion est assurée de réussir, on lui préfèrera <strong>static_cast&lt;&gt;</strong> plus rapide et ne nécessitant pas que les classes possèdent une méthode virtuelle.</p>
<h1>const_cast&lt;&gt;</h1>
<p><strong>const_cast&lt;&gt;</strong> permet de <strong>supprimer</strong> l&#8217;attribut <em>constant</em> ou <em>volatile</em> d&#8217;une <strong>référence</strong> ou d&#8217;un <strong>type pointé</strong>. Exemple : <strong>const char*</strong> vers <strong>char*</strong> ou <strong>volatile int</strong> vers <strong>int</strong>.</p>
<p>C&#8217;est notamment <strong>le seul</strong> opérateur de conversion à pouvoir le faire : même <strong>reinterpret_cast&lt;&gt;</strong> n&#8217;a pas ce pouvoir.</p><pre class="crayon-plain-tag">void foo()
{
char* buf = new char[16];

// L'ajout de l'attribut const ne n&eacute;cessite pas de cast
// Il est implicite
const char* cbuf = buf;

// Le retrait de l'attribut const n&eacute;cessite const_cast&amp;lt;&amp;gt;.
char* buf2 = const_cast&amp;lt;char*&amp;gt;(cbuf);
}</pre><p></p>
<h2>L&#8217;importance d&#8217;écrire un code &#8220;const-correct&#8221;</h2>
<p>Directement relié aux opérateurs de conversion, l&#8217;écriture d&#8217;un code const-correct est un autre aspect du C++ souvent mal perçu par les programmeurs C. Le C est plus ancien et le mot clé <strong>const</strong> n&#8217;y a pas toujours existé; il a été emprunté au C++ par la suite.</p>
<p>Le fait d&#8217;indiquer qu&#8217;une variable est constante est un outil puissant permettant au compilateur de nous signaler certaines de nos erreurs qui auraient autrement passé la barrière de la compilation.</p>
<p>Qui ne s&#8217;est jamais trompé dans l&#8217;ordre des arguments d&#8217;un <strong>memcpy()</strong> ?</p><pre class="crayon-plain-tag">void foo()
{
const char* source = &quot;blog.freelan.org&quot;;
char* destination = new char[16];

// Oups ! Je me suis tromp&eacute; dans l'ordre des arguments...
// ... heureusement, puisque memcpy sp&eacute;cifie que le premier param&egrave;tre est un void*
// et pas un const void*, le compilateur m'indique une erreur.
memcpy(source, destination, strnlen(source));
}</pre><p></p>
<h2>Les mots clé &#8220;const&#8221; ou &#8220;volatile&#8221; appliqués aux classes</h2>
<p>En C++, les mots clé <strong>const</strong> et <strong>volatile</strong> s&#8217;appliquent évidemment aussi aux instances de classes mais ont des sémantiques différentes :</p>
<p>Le caractère <strong>const</strong> ou <strong>volatile</strong> s&#8217;applique récursivement aux membres de l&#8217;instance.</p>
<p>Il n&#8217;est possible d&#8217;appeler une méthode d&#8217;une classe que dans les cas suivants :</p>
<ul>
<li>l&#8217;instance n&#8217;est pas <strong>const</strong>.</li>
<li>l&#8217;instance est <strong>const</strong> et la méthode est déclarée <strong>const</strong>.</li>
<li>l&#8217;instance est déclarée <strong>volatile</strong> et la méthode est déclarée <strong>volatile</strong>.</li>
<li>l&#8217;instance est déclarée <strong>const</strong> et <strong>volatile</strong> et la méthode est elle aussi déclarée <strong>const</strong> et <strong>volatile</strong>.</li>
</ul>
<h2>À propos de &#8220;volatile&#8221;</h2>
<p>Certains lecteurs peuvent être perdus à la lecture du mot clé <strong>volatile</strong> qui, il faut bien l&#8217;avouer, n&#8217;est pas utilisé très souvent. Décrire précisément le rôle de <strong>volatile</strong> mériterait un article bien à part mais je vais tout de même dire en deux mots à quoi il sert :</p>
<p>Lorsqu&#8217;une variable est déclarée <strong>volatile</strong>, le compilateur n&#8217;a pas le droit d&#8217;optimiser sa valeur (mise en cache processeur) lors de tests.</p>
<p>Ainsi sans <strong>volatile</strong> sur la variable <strong>do_loop</strong>, le code suivant :</p><pre class="crayon-plain-tag">void foo()
{
bool do_loop = true;
while (do_loop) { doSomething(); }
}</pre><p>Risquerait d&#8217;être optimisé en tant que :</p><pre class="crayon-plain-tag">void foo()
{
while (true) { doSomething(); }
}</pre><p>Ce qui est correct dans la plupart des cas&#8230; sauf si <strong>do_loop</strong> peut être modifié par un autre <strong>thread</strong>. C&#8217;est principalement dans ce genre de cas que <strong>volatile</strong> trouve son utilité.</p>
<h2>Erreurs courantes</h2>
<p>Une erreur courante concernant <strong>const_cast&lt;&gt;</strong> consiste à supposer que l&#8217;on peut <em>toujours</em> supprimer le caractère <strong>constant</strong> d&#8217;une variable.</p>
<p>Ceci est évidemment faux : on ne peut supprimer le caractère constant (respectivement volatile) d&#8217;une variable que lorsque celle-ci a été déclarée <strong>non-const</strong> (respectivement <strong>non-volatile</strong>).</p>
<p>Ainsi le code suivant a un comportement indéfini :</p><pre class="crayon-plain-tag">void foo()
{
const char* cbuf = &quot;blog.freelan.org&quot;;

// L'utilisation de const_cast&amp;lt;&amp;gt; ici a un comportement ind&eacute;fini (undefined behavior)
// car cbuf a &eacute;t&eacute; d&eacute;clar&eacute; const.
char* buf = const_cast&amp;lt;char*&amp;gt;(cbuf);
}</pre><p>Un autre cas courant est celui des variables membres qui servent à mettre en cache un résultat :</p><pre class="crayon-plain-tag">class MyClass
{
public:

MyClass() : m_value_cache(0) {}

int getValue() const
{
if (m_value_cache == 0)
const_cast&amp;lt;int&amp;amp;&amp;gt;(m_value_cache) = computeValue();

return m_value_cache;
}

private:

static int computeValue();

int m_value_cache;
};</pre><p>L&#8217;utilisation de <strong>const_cast&lt;&gt;</strong> ici est erronée : si on déclare une instance <strong>const</strong> de MyClass, m_value_cache est aussi <strong>const</strong> lors de sa définition. L&#8217;utilisation de <strong>const_cast&lt;&gt;</strong> est la même que dans l&#8217;exemple précédent et a <strong>comportement indéfini</strong>.</p>
<p>La bonne solution est d&#8217;utiliser le mot clé <strong>mutable</strong>, qui permet à une variable membre de ne pas avoir les mêmes contraintes <strong>const</strong>/<strong>volatile</strong> que son instance parente :</p><pre class="crayon-plain-tag">class MyClass
{
public:

MyClass() : m_value_cache(0) {}

int getValue() const
{
// m_value_cache est d&eacute;clar&eacute; mutable
// il peut donc changer, m&ecirc;me dans une m&eacute;thode const
if (m_value_cache == 0)
m_value_cache = computeValue();

return m_value_cache;
}

private:

static int computeValue();

// Utilisation du mot cl&eacute; &quot;mutable&quot;
mutable int m_value_cache;
};</pre><p></p>
<h2>En bref</h2>
<p><strong>const_cast&lt;&gt;</strong> est le seul opérateur de conversion à pouvoir supprimer le caractère <strong>const</strong> ou <strong>volatile</strong> d&#8217;une variable. L&#8217;utilisation de <strong>const_cast&lt;&gt;</strong> doit rester très rare : le contraire indique souvent une importante erreur de design. Son seul usage habituellement toléré est l&#8217;interfaçage avec des bibliothèques historiques qui ne sont pas <strong>const-correct</strong>.</p>
<h1>reinterpret_cast&lt;&gt;</h1>
<p>Il s&#8217;agit de l&#8217;opérateur de conversion le plus dangereux, et du plus mal utilisé. Son rôle est de dire au compilateur : &#8220;réinterprète-moi la représentation binaire de ce type en tant qu&#8217;un autre type&#8221;.</p>
<p>Il permet :</p>
<ul>
<li>De convertir <strong>n&#8217;importe quel</strong> type pointé en une autre, même lorsque ceux-ci n&#8217;ont <strong>aucun rapport</strong>. Exemple : <strong>int*</strong> vers <strong>double*</strong>.</li>
<li>De convertir un type pointé en sa <strong>représentation intégrale</strong> et vice et versa. Exemple : <strong>int*</strong> vers <strong>int</strong>.</li>
</ul>
<p>Il est à noter que ces conversions sont <strong>dépendantes de l&#8217;implémentation</strong>. En d&#8217;autres termes, le compilateur est libre de faire ce qu&#8217;il veut concernant la conversion basée sur <strong>reinterpret_cast&lt;&gt;</strong> mais ce comportement doit être constant : il ne s&#8217;agit <strong>pas</strong> de comportement indéfini; le comportement est bien défini, simplement pas par le standard C++ mais votre version du compilateur. Si vous vous basez sur cette dépendance de l&#8217;implémentation, votre code est donc <strong>non-portable</strong>.</p>
<p>La seule garantie délivrée par le standard C++ concernant <strong>reinterpret_cast&lt;&gt;</strong> est que si vous convertissez un type A en un type B, puis de nouveau en un type A, le comportement est bien défini et vous récupérez bien la valeur de départ.</p>
<p>On comprend dès lors facilement le danger que peut représenter <strong>reinterpret_cast&lt;&gt;</strong>.</p>
<p>Voici un exemple d&#8217;utilisation :</p><pre class="crayon-plain-tag">void foo()
{
int a = 5;

// Le contenu de b d&eacute;pend du compilateur utilis&eacute;
// et n'est pas d&eacute;fini par le standard
double* b = reinterpret_cast&amp;lt;double*&amp;gt;(&amp;amp;a);

// Ici, on a la garantie que *c vaudra a, c'est &agrave; dire 5.
int* c = reinterpret_cast&amp;lt;int*&amp;gt;(b);
}</pre><p></p>
<h2>Cas particuliers</h2>
<p>Le peu de garanties associées à <strong>reinterpret_cast&lt;&gt;</strong> rendent celui-ci quasiment inutile dans la plupart des cas. Il y a cependant certaines exceptions de fait qui justifient une utilisation de <strong>reinterpret_cast&lt;&gt;</strong> sans nuire à la portabilité :</p>
<p>Les conversions entre les types <strong>char*</strong> et <strong>unsigned char*</strong> bien que non spécifiées par le standard, sont en pratique supportées par tous les compilateurs et produisent le comportement attendu. Le compilateurs ont par ailleurs de plus fortes contraintes à leur égard (spécifiquement au niveau de leur représentation) pour des raisons de compatibilité ascendante avec le C.</p>
<p>Vous pouvez donc clairement supposer qu&#8217;un <strong>reinterpret_cast&lt;&gt;</strong> entre un <strong>char*</strong> et un <strong>unsigned char*</strong> sera à la fois portable et défini.</p>
<h2>Polymorphisme</h2>
<p><strong>reinterpret_cast&lt;&gt;</strong> utilisé dans le cadre d&#8217;une conversion faisant intervenir du polymorphisme a un <strong>comportement non défini</strong>. Il n&#8217;est ainsi pas correct d&#8217;effectuer un <strong>reinterpret_cast&lt;&gt;</strong> entre par exemple un <strong>Base*</strong> et un <strong>Derived*</strong>.</p>
<h2>En bref</h2>
<p><strong>reinterpret_cast&lt;&gt;</strong> est l&#8217;opérateur de conversion le plus dangereux : permettant de faire ce qu&#8217;aucun autre ne peut faire (des conversions entres des types non liés) il convient de l&#8217;utiliser avec la plus grande prudence. En pratique, on lui préfèrera <strong>static_cast&lt;&gt;</strong> qui permet d&#8217;effectuer des conversions <strong>plus sûres</strong>, y compris vers et depuis des types pointés génériques (<strong>void*</strong>). Son seul usage toléré est l&#8217;interfaçage avec du code C ancien qui utilise pour ses paramètres de &#8220;buffer&#8221; des <strong>char*</strong> ou <strong>unsigned char*</strong> au lieu des <strong>void*</strong>.</p>
<h1>Old-school : les conversions &#8220;à la C&#8221;</h1>
<p>Le C++ supporte toujours l&#8217;ancienne syntaxe concernant les conversions &#8220;à la façon C&#8221;. Cependant, le standard précise clairement l&#8217;effet d&#8217;une telle conversion :</p>
<p>Le &#8220;cast&#8221; suivant : <strong>(Type)valeur</strong> ou <strong>Type(valeur)</strong></p>
<p>Sera équivalent à, par ordre de préférence :</p>
<ol>
<li>un <strong>const_cast&lt;&gt;</strong></li>
<li>un <strong>static_cast&lt;&gt;</strong></li>
<li>un <strong>static_cast&lt;&gt;</strong> suivi d&#8217;un <strong>const_cast&lt;&gt;</strong></li>
<li>un <strong>reinterpret_cast&lt;&gt;</strong></li>
<li>un <strong>reinterpret_cast&lt;&gt;</strong> suivi d&#8217;un <strong>const_cast&lt;&gt;</strong></li>
</ol>
<p>Les bonnes pratiques indiquent souvent que l&#8217;utilisation de ce type de conversion est à bannir, principalement parce qu&#8217;il peut résulter <em>silencieusement</em> en un <strong>reinterpret_cast&lt;&gt;</strong>, qui comme nous l&#8217;avons vu, peut se révéler <strong>extrêmement dangereux</strong>. De plus, l&#8217;usage des alternatives modernes aux opérateurs de conversion permet de <em>spécifier clairement l&#8217;intention du programmeur</em> et de <em>protéger contre les erreurs involontaires</em> (comme celles que nous avons vu avec <strong>const_cast&lt;&gt;</strong>).</p>
<h2>Une autre utilité</h2>
<p>Les &#8220;casts&#8221; à la C offrent également une possibilité qui n&#8217;est permise par <strong>aucun autre</strong> opérateur de conversion : celle de convertir vers une classe de base au travers d&#8217;un <strong>héritage privé</strong>. Ce type d&#8217;héritage est très souvent critiqué et fortement déconseillé. Je ne détaillerai pas ici les conséquences et les raisons de ce type d&#8217;héritage; c&#8217;est un sujet qui mérite son propre article.</p>
<h1>Conclusion</h1>
<p>Il y a beaucoup à dire sur les opérateurs de conversion et encore plus à apprendre. Nous avons vu que bien utilisés, ils sont un outil puissant et un allié du programmeur. Protégeant contre les erreurs involontaires et révélant les erreurs de conception, ils restent pour certains dangereux et sont tout de même à utiliser avec la plus grande précaution.</p>
<p>Une bonne connaissance de ces opérateurs de conversion et de leurs limites reste indispensable à la réalisation de programmes maintenables en C++.</p>
<h1>Références</h1>
<p>Voici une série de liens (en anglais, pour la plupart) qui m&#8217;ont inspiré dans la rédaction de cet article.</p>
<ul>
<li><a href="http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast">http://stackoverflow.com/questions/28002/regular-cast-vs-static-cast-vs-dynamic-cast</a></li>
<li><a href="http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-and-reinterpret-cast-be-used">http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-and-reinterpret-cast-be-used</a></li>
<li><a href="http://stackoverflow.com/questions/4708444/is-there-a-good-way-to-convert-from-unsigned-char-to-char">http://stackoverflow.com/questions/4708444/is-there-a-good-way-to-convert-from-unsigned-char-to-char</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/5f6c9f8h%28v=VS.80%29.aspx">http://msdn.microsoft.com/en-us/library/5f6c9f8h%28v=VS.80%29.aspx</a></li>
</ul>
<p>N&#8217;hésitez pas à les consulter pour obtenir d&#8217;autres informations. Je vous recommande également de vous inscrire sur Stack Overflow qui est à mon sens le meilleur site de questions/réponses concernant la programmation : le niveau des questions et surtout des réponses y est vraiment très élevé.</p>
<p>Comme toujours bien sûr, vous pouvez également utiliser les commentaires pour obtenir des précisions sur un point ou l&#8217;autre.</p>
<p>Merci pour votre lecture !</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/01/17/les-casts-en-cpp/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>La directive &#8220;inline&#8221; démystifiée</title>
		<link>https://blog.freelan.org/2011/01/11/la-directive-inline-demystifiee/</link>
		<comments>https://blog.freelan.org/2011/01/11/la-directive-inline-demystifiee/#comments</comments>
		<pubDate>Tue, 11 Jan 2011 14:32:54 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[C++]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[inline]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[macosx]]></category>
		<category><![CDATA[unix]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=169</guid>
		<description><![CDATA[Le C++ est sans conteste l'un des langages les plus complets mais aussi les plus complexes existant dans le monde du développement en entreprise. Ses grandes flexibilité et diversité en font à la fois un langage puissant et dangereux. Il ne s'agit pas ici d'en faire une nouvelle présentation; de nombreux ouvrages lui sont déjà consacrés : qu'il s'agisse des "design patterns" ou de fonctionnalités générales, il y en a vraiment pour tous les goûts.

Cependant, j'ai décidé aujourd'hui de traiter d'un point en particulier, souvent mal perçu par les débutants et parfois même par des gens plus expérimentés : il s'agit de la directive inline.]]></description>
				<content:encoded><![CDATA[<p>Le C++ est sans conteste l&#8217;un des langages les plus complets mais aussi les plus complexes existant dans le monde du développement en entreprise. Ses grandes flexibilité et diversité en font à la fois un langage puissant et dangereux. Il ne s&#8217;agit pas ici d&#8217;en faire une nouvelle présentation; de nombreux ouvrages lui sont déjà consacrés : qu&#8217;il s&#8217;agisse des &#8220;design patterns&#8221; ou de fonctionnalités générales, il y en a vraiment pour tous les goûts.</p>
<p>Cependant, j&#8217;ai décidé aujourd&#8217;hui de traiter d&#8217;un point en particulier, souvent mal perçu par les débutants et parfois même par des gens plus expérimentés : il s&#8217;agit de la directive <strong>inline</strong>.</p>
<h1>Piqûre de rappel</h1>
<p>Avant d&#8217;avancer sur le chemin de &#8220;l&#8217;inlining&#8221;, rappelons quelques principes élémentaires du C++.</p>
<p><em>Remarque : En tant que programmeur expérimenté, vous connaissez probablement déjà tout ce qui suit. Vous devriez tout de même prendre le temps de lire cette partie pour deux raisons : la première, ça ne fait jamais de mal. Et la deuxième : si jamais j&#8217;écrivais une bêtise, vous pourriez gentiment me le faire remarquer ! <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_biggrin.gif" alt=":D" class="wp-smiley" /></em></p>
<p>Le C++ est un langage <strong>compilé</strong> (par opposition à langage <strong>interprété</strong>), ce qui signifie qu&#8217;il induit la génération d&#8217;un &#8220;binaire&#8221; lors d&#8217;une phase appelée <strong>compilation</strong>. Ce binaire peut être un fichier exécutable (.exe sous Windows), une bibliothèque (.so/.a sous Unix, .lib/.dll sous Windows) ou un fichier objet intermédiaire (.o sous Unix, .obj sous Windows).</p>
<p>La phase que l&#8217;on nomme &#8220;compilation&#8221; est en fait séparée en trois étapes successives :</p>
<ol>
<li>Le prétraitement (ou &#8220;preprocessing&#8221;), qui va se charger de remplacer les différentes <strong>macros</strong> présentes dans le code par leur véritable valeur. Le résultat de ce prétraitement est passé au &#8220;compilateur&#8221;.</li>
<li>La compilation, qui transforme le code pré-traité en langage machine au sein de fichiers objets. En pratique, il y a un fichier objet généré par <em>unité de traduction</em> (ou &#8220;translation unit&#8221;).</li>
<li>L&#8217;édition des liens, qui rassemble les fichiers objets générés au sein d&#8217;une seule entité (une <em>bibliothèque dynamique</em> ou un <em>exécutable</em>). Si on a déclaré et utilisé une fonction mais que son implémentation est absente, cette étape ne passe pas.</li>
</ol>
<p><em>Remarque : Habituellement, dans le cas d&#8217;une <strong>bibliothèque statique</strong>, l&#8217;édition des liens n&#8217;est pas effectuée : il s&#8217;agit d&#8217;une simple concaténation des fichiers objets.</em></p>
<p>Les bonnes pratiques du C++ dictent ensuite que lorsque l&#8217;on écrit le code d&#8217;une classe, on place sa <strong>définition</strong> (et donc sa <strong>déclaration</strong>) dans un fichier dit &#8220;<em>header</em>&#8220;, et son <strong>implémentation</strong> dans un fichier &#8220;<em>source</em>&#8220;.</p>
<p>Il existe une règle nommée &#8220;règle de la définition unique&#8221; (ou ODR : &#8220;<a href="http://en.wikipedia.org/wiki/One_Definition_Rule">One Definition Rule</a>&#8220;) qui dit que l&#8217;on peut <strong>déclarer</strong> autant de fois que l&#8217;on veut une classe, une fonction, etc. mais qu&#8217;on ne peut la <strong>définir </strong>qu&#8217;une seule fois. Nous verrons plus tard en quoi <strong>inline</strong> influe à ce niveau.</p>
<h2>Un exemple simple</h2>
<p>Prenons un exemple tout simple avec une classe &#8220;Person&#8221; qui représente<em>&#8230;</em> une personne. <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>Voici le fichier <em>header</em> :</p><pre class="crayon-plain-tag">/**
 * \file person.hpp
 * \author Julien Kauffmann
 * \brief A person.
 */

#ifndef PERSON_HPP
#define PERSON_HPP

#include &lt;string&gt;

class Person
{
  public:

    /**
     * \brief Create a person given its name.
     * \param name The person name.
     */
    Person(const std::string&amp; name);

    /**
     * \brief Get the name.
     * \return The name.
     */
    const std::string&amp; name() const;

  private:

    /**
     * \brief The name.
     */
    std::string m_name;
};

#endif /* PERSON_HPP */</pre><p>Dans ce header, nous avons <strong>déclaré</strong> et <strong>défini</strong> le type <em>Person</em>.</p>
<p>Son implémentation, elle, va dans le fichier source :</p><pre class="crayon-plain-tag">/**
 * \file person.cpp
 * \author Julien Kauffmann
 * \brief A person.
 */

#include &quot;person.hpp&quot;

Person::Person(const std::string&amp; _name) :
  m_name(_name)
{
}

const std::string&amp; Person::name() const
{
  return m_name;
}</pre><p>Si nous reprenons les trois étapes de la compilation, voici ce que se passe pour chacun des fichiers :</p>
<p>Le processus commence par le choix de l&#8217;unité de traduction à compiler : ici, il s&#8217;agit du fichier &#8220;person.cpp&#8221;.</p>
<ul>
<li>Le préprocesseur analyse chaque ligne, procède à l&#8217;inclusion du fichier &#8220;person.hpp&#8221; (directive #include) tel qu&#8217;on le ferait avec un copier-coller. Au passage, tous les commentaires dans les fichiers sont supprimés, et les éventuelles <strong>macros</strong> sont remplacées.</li>
</ul>
<p>On se retrouve avec un fichier qui se rapproche théoriquement de ça :</p><pre class="crayon-plain-tag">// J'omets volontairement la d&eacute;finition de std::string qui est *un peu* longue !

class Person
{
  public:
    Person(const std::string&amp; name);
    const std::string&amp; name() const;

  private:
    std::string m_name;
};

Person::Person(const std::string&amp; _name) :
  m_name(_name)
{
}

const std::string&amp; Person::name() const
{
  return m_name;
}</pre><p></p>
<ul>
<li>Le compilateur vérifie la syntaxe de l&#8217;ensemble du fichier et compile chaque implémentation de fonction (ou méthode) qu&#8217;il rencontre. Ici, les codes du constructeur Person::Person() et du &#8220;getter&#8221; name() sont effectivement transformés en langage machine au sein d&#8217;un fichier objet.</li>
<li>Enfin, si le programme fait référence à Person::Person() ou Person::name(), l&#8217;édition des liens associera l&#8217;appel de fonction à son adresse effective.</li>
</ul>
<h1>Les idées fausses sur la directive &#8220;inline&#8221;</h1>
<p>S&#8217;en suit ici un florilège des idées reçues que j&#8217;ai déjà entendu (ou prononcé :D) ça et là sur <strong>inline</strong> :</p>
<ul>
<li>&#8220;Ça sert à ordonner au compilateur de ne jamais compiler le code de la fonction.&#8221;</li>
<li>&#8220;C&#8217;est quand on écrit directement du code dans la définition d&#8217;une classe.&#8221;</li>
<li>&#8220;C&#8217;est pour accélérer les appels à une fonction.&#8221;</li>
<li>&#8220;Ça sert à déclarer des macros intelligentes.&#8221;</li>
<li>&#8220;Ça indique que la fonction a une liaison interne.&#8221;</li>
</ul>
<p>En réalité, voici la raison d&#8217;être du mot clé <strong>inline</strong>, telle que définie par <em><a href="http://fr.wikipedia.org/wiki/Bjarne_Stroustrup">Bjarne  Stroustrup</a></em> :</p>
<blockquote><p>The <code>inline</code> specifier is a hint to the compiler that it  should attempt to generate code for a call of the inline  function rather than laying down the code for the function once and then calling  through the usual function call mechanism.</p></blockquote>
<p>Pour ceux que l&#8217;anglais rebute :</p>
<blockquote><p>La directive <code>inline</code> est une information donnée au compilateur lui indiquant qu&#8217;il devrait essayer de générer du code pour chaque appel de la fonction plutôt que de générer une seule fois le code de façon générique et d&#8217;utiliser le mécanisme habituel d&#8217;appel de fonction.</p></blockquote>
<p>En gros, on apprend que la directive <strong>inline</strong> n&#8217;est <em>pas un ordre</em>, mais une simple <strong>indication</strong>, que le compilateur est d&#8217;ailleurs libre de refuser. Souvenez-vous que c&#8217;est son travail d&#8217;optimiser le code généré, pas le vôtre.</p>
<p>En pratique, on utilisera donc pas <strong>inline</strong> pour des raisons d&#8217;optimisation, mais simplement pour modifier la &#8220;One Definition Rule&#8221;. En effet, là où une fonction ne doit habituellement avoir <strong>qu&#8217;une seule définition</strong> parmi <em>toutes</em> les unités de traductions, le fait de la rendre<strong> inline</strong> change la règle et indique que la fonction <strong>doit avoir la même définition</strong> dans <em>chacune</em> des unités de traduction qui l&#8217;utilise.</p>
<h2>Un exemple</h2>
<p>Prenons pour exemple le célèbre cas de la fonction factorielle.</p>
<p><em>Remarque : Le choix de cet exemple n&#8217;est pas innocent. <a href="http://fr.wikipedia.org/wiki/Bjarne_Stroustrup">Bjarne Stroustrup</a> utilise lui-même cet exemple lorsqu&#8217;il parle de la directive <strong>inline</strong>.</em></p>
<p>Une implémentation naïve de factorielle est la suivante :</p><pre class="crayon-plain-tag">inline int factorial(int n)
{
 return (n &lt; 2) ? 1 : n * factorial(n - 1);
}</pre><p><em>Note : Cette fonction n&#8217;est pas optimale (on répète inutilement le test &#8220;(n <= 1)" à chaque itération. Mais elle convient très bien pour notre exemple.
</em></p>
<p>Supposons que cette fonction est déclarée dans un header de notre bibliothèque et qu&#8217;un utilisateur de cette bibliothèque utilise quelque-part dans son code la fonction, par exemple :</p><pre class="crayon-plain-tag">int main()
{
  int f = factorial(6);
  std::cout &lt;&lt; &quot;Factorial(6) = &quot; &lt;&lt; f &lt;&lt; std::endl;
  return EXIT_SUCCESS;
}</pre><p>Lors de la compilation de ce code, il peut se passer plusieurs choses :</p>
<ol>
<li>Le compilateur peut décider de compiler la fonction <em>factorial</em> comme n&#8217;importe qu&#8217;elle autre fonction. Elle aura en pratique un passage de paramètre, une pile d&#8217;appel etc.</li>
<li>Ou il peut décider de remplacer <em>factorial(6)</em> par <em>6 * factorial(5)</em> directement.</li>
<li>Enfin, un compilateur très intelligent peut carrément décider &#8220;d&#8217;inliner&#8221; complètement l&#8217;appel et de remplacer <em>factorial(6)</em> par <em>720</em>, optimisant de ce fait drastiquement le programme.</li>
</ol>
<p>On notera que l&#8217;appel d&#8217;une fonction <strong>inline</strong> est sémantiquement identique à celle d&#8217;une fonction &#8220;classique&#8221;. Il est possible d&#8217;en hériter, de la surcharger, etc.</p>
<h1>Utilisation au quotidien</h1>
<p>Voici quelques usages corrects de fonctions &#8220;<strong>inline</strong>&#8221; :</p><pre class="crayon-plain-tag">namespace Foo
{
	class Bar
	{
		public:

			int value() const { return m_value; }

			int add(int a) const;

			template
			T sub(T a)
			{
				return m_value - a;
			}

		private:

			int m_value;
	};

	inline Bar::add(int a) const { return m_value + a; }
}</pre><p></p>
<ul>
<li>Dans le premier cas, la méthode value() est directement définie au sein de la définition de la classe. Elle est <em>implicitement</em> déclarée <strong>inline</strong>. L&#8217;ajout du mot clé <strong>inline</strong> serait <em>redondant</em> et donc inutile.</li>
<li>Dans le second cas, la méthode add() est simplement déclarée (sans mot clé particulier) au sein de la classe. Sa définition est écrite dans le fichier <em>header</em>, en dehors de celle de la classe, mais toujours dans le même <em>namespace</em>, tel qu&#8217;on le ferait si on implémentait cette fonction dans le fichier source. Dans ce cas, la <strong>définition</strong> de la fonction étant écrite au sein même du fichier header (et donc potentiellement présente dans plusieurs unités de traduction), on <strong>doit</strong> cependant ajouter le mot clé <strong>inline</strong> pour s&#8217;affranchir de la &#8220;<em>One Definition Rule</em>&#8220;.</li>
<li>Enfin, dans le dernier cas, la méthode sub n&#8217;est pas une vraie méthode mais un template. L&#8217;ajout de la directive <strong>inline</strong> n&#8217;est <em>pas obligatoire</em>, car encore une fois, la définition de la méthode se situe dans la définition de la classe. Elle est donc implicitement <strong>inline</strong>.</li>
</ul>
<p>N&#8217;utilisez <strong>inline</strong> que sur de très petites fonctions (notion subjective mais en gros : si votre fonction fait plus qu&#8217;une simple opération arithmétique ou un retour de valeur, elle n&#8217;a surement pas d&#8217;intérêt à être <strong>inline</strong>) et si possible, uniquement sur celles qui ont vocation à être appelées souvent. Les meilleurs candidats pour <strong>inline</strong> sont généralement bien sûr les <em>getters</em>, les <em>setters</em>, ou encore les <em>destructeurs virtuels vides</em>.</p>
<h1>Conclusion</h1>
<p>La première fois que l&#8217;on m&#8217;a parlé du mot clef <strong>inline</strong>, on me l&#8217;a présenté comme un moyen d&#8217;optimiser les appels de fonction. Pendant très longtemps, j&#8217;ai d&#8217;ailleurs soutenu cette version aveuglement. Mais nous avons vu aujourd&#8217;hui que les compilateurs sont suffisamment capables pour déterminer d&#8217;eux-même quand, quoi et comment optimiser.</p>
<p>En pratique, on retiendra que de bonnes connaissances concernant la &#8220;One Definition Rule&#8221; et la directive <strong>inline</strong> sont indispensables à l&#8217;écriture d&#8217;un code réutilisable et maintenable.</p>
<p>J&#8217;espère que cet article vous aura appris quelque-chose (ou à défaut intéressé). N&#8217;hésitez pas à me signaler dans les commentaires les éventuelles erreurs que j&#8217;aurais pu commettre.</p>
<p><strong>Bon code !</strong></p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2011/01/11/la-directive-inline-demystifiee/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Personnaliser la barre d&#8217;action rapide sous Windows Vista/Seven</title>
		<link>https://blog.freelan.org/2010/12/21/personnaliser-la-barre-daction-rapide-sous-windows-vistaseven/</link>
		<comments>https://blog.freelan.org/2010/12/21/personnaliser-la-barre-daction-rapide-sous-windows-vistaseven/#comments</comments>
		<pubDate>Tue, 21 Dec 2010 15:52:38 +0000</pubDate>
		<dc:creator><![CDATA[Julien Kauffmann]]></dc:creator>
				<category><![CDATA[Astuces]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[administrateur]]></category>
		<category><![CDATA[barre d'action]]></category>
		<category><![CDATA[commande]]></category>
		<category><![CDATA[personnalisation]]></category>
		<category><![CDATA[regedit]]></category>
		<category><![CDATA[registre]]></category>
		<category><![CDATA[seven]]></category>
		<category><![CDATA[vista]]></category>
		<category><![CDATA[windows]]></category>

		<guid isPermaLink="false">http://blog.freelan.org/?p=144</guid>
		<description><![CDATA[Le shell Windows a bien changé. Il offre nativement sous Vista (et encore plus sous Seven) un bon nombre de raccourcis pour des fonctions utilisées souvent. Toutefois, ses capacités de personnalisation pour un utilisateur lambda restent encore hélas bien limitées.

Dans cet article, je vais vous montrer comment personnaliser la barre d'action rapide pour y ajouter des fonctions "maison".]]></description>
				<content:encoded><![CDATA[<p>Aussi longtemps que je me souvienne, j&#8217;ai toujours été très intéressé par la personnalisation. Quand je débutais la programmation, mes premiers programmes étaient des alternatives au &#8220;shell Windows&#8221; (explorer.exe). Évidemment, mes programmes de remplacement étaient pour la plupart encore plus pauvres que ceux à remplacer et ne faisaient généralement pas long feu ! Mais il s&#8217;agissait d&#8217;un bon exercice.</p>
<p>Aujourd&#8217;hui, le shell Windows a bien changé. Il offre nativement sous Vista (et encore plus sous Seven) un bon nombre de raccourcis pour des fonctions utilisées souvent. Toutefois, ses capacités de personnalisation pour un utilisateur lambda restent encore hélas bien limitées.</p>
<p>Dans cet article, je vais vous montrer comment personnaliser la barre d&#8217;action rapide pour y ajouter des  fonctions &#8220;maison&#8221;.</p>
<div id="attachment_145" style="width: 601px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/action-bar.png"><img class="size-full wp-image-145" title="Barre d'action" src="http://blog.freelan.org/wp-content/uploads/2010/12/action-bar.png" alt="Barre d'action" width="591" height="41" /></a><p class="wp-caption-text">La barre d&#39;action modifiée</p></div>
<h1>Le principe</h1>
<p>Cette modification n&#8217;est <strong>pas officiellement supportée</strong> par Microsoft (ou en tout cas, je n&#8217;ai rien trouvé qui en parlait), mais elle reste relativement propre. En d&#8217;autres termes, si c&#8217;est pour modifier votre propre poste de travail et pour votre confort personnel, allez-y, mais si vous souhaitez baser une application commerciale dessus, c&#8217;est <strong>à vos risques et périls !</strong> <img src="https://blog.freelan.org/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>Le paramétrage de la barre d&#8217;action, comme beaucoup d&#8217;autres choses sous Windows, se fait par une modification de la base de registre. Si vous n&#8217;êtes pas déjà familier avec la base de registre Windows, je vous recommande sincèrement de ne pas continuer.</p>
<p>Chaque &#8220;type&#8221; de répertoire possède une clé unique associée à un paramétrage. Nous allons modifier les paramètres des dossiers de type &#8220;générique&#8221; pour que nos nouvelles commandes apparaissent à coté des commandes habituelles.</p>
<h1>Les mains dans le camboui</h1>
<p>Ouvrez, <strong>en tant qu&#8217;administrateur</strong>, l&#8217;éditeur de base de registre (&#8220;regedit&#8221;).</p>
<p>Placez-vous dans la clé suivante :</p><pre class="crayon-plain-tag">HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FolderTypes</pre><p>Puis localisez la clé nommée :</p><pre class="crayon-plain-tag">{5c4f28b5-f869-4e84-8e60-f11db97c5cc7}</pre><p>Il s&#8217;agit du <a href="http://fr.wikipedia.org/wiki/Globally_Unique_Identifier">GUID</a> associé aux répertoires génériques. Ouvrez cette clé; vous devriez obtenir une fenêtre qui ressemble à ça :</p>
<div id="attachment_152" style="width: 656px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/regedit.png"><img class="size-full wp-image-152 " title="Éditeur de base de registre" src="http://blog.freelan.org/wp-content/uploads/2010/12/regedit.png" alt="Éditeur de base de registre" width="646" height="353" /></a><p class="wp-caption-text">Paramètres pour les dossiers génériques</p></div>
<p>Si vous n&#8217;avez pas de sous-clé nommée &#8220;TasksNoItemsSelected&#8221; ne vous inquiétez pas, c&#8217;est même plutôt normal. Nous allons justement la créer.</p>
<p>Par défaut, la clé sélectionnée a pour propriétaire l&#8217;utilisateur système &#8220;TrustedInstaller&#8221;, utilisé uniquement lors des mises à jour Windows. Pour ajouter nos propres clés, il nous faut nous rendre propriétaire de cette clé, et de ses sous-clés.</p>
<p>Faites un clic droit sur la clé &#8220;{5c4f28b5-f869-4e84-8e60-f11db97c5cc7}&#8221;, puis choisissez le menu &#8220;Autorisations&#8221;.</p>
<div id="attachment_155" style="width: 387px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/autorisations.png"><img class="size-full wp-image-155" title="Autorisations" src="http://blog.freelan.org/wp-content/uploads/2010/12/autorisations.png" alt="Autorisations" width="377" height="456" /></a><p class="wp-caption-text">La fenêtre d&#39;autorisations</p></div>
<p>Cliquez sur &#8220;Avancé&#8221; puis, dans l&#8217;onglet &#8220;Propriétaire&#8221;, sélectionnez votre compte comme nouveau propriétaire. N&#8217;oubliez pas de cocher la case marquée &#8220;Remplacer le propriétaire des sous-conteneurs et des objets&#8221; puis validez par &#8220;OK&#8221;.</p>
<p>Une fois devenu propriétaire de la clé, vous pouvez désormais créer des sous-clés.</p>
<h2>Un exemple avec Powershell</h2>
<p>Nous allons, pour exemple ajouter une action qui lance, dans le répertoire ouvert sous l&#8217;explorateur, une commande Powershell correctement positionnée. Cet exemple est bien entendu très facilement adaptable pour d&#8217;autres besoins.</p>
<p>Commençons par créer la sous-clé &#8220;TasksNoItemsSelected&#8221; si elle n&#8217;existe pas déjà.</p>
<p>Puis créez une sous-clé nommée &#8216;0&#8217; pour la première commande que nous ajoutons. Pour les prochaines commandes, il faudra bien entendu remplacer &#8216;0&#8217; par &#8216;1&#8217;, &#8216;2&#8217;, &#8216;3&#8217;, etc.</p>
<p>Ajoutez toutes les clés requises pour obtenir l&#8217;arborescence suivante :</p>
<div id="attachment_157" style="width: 347px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/tree.png"><img class="size-full wp-image-157" title="Arborescence" src="http://blog.freelan.org/wp-content/uploads/2010/12/tree.png" alt="Arborescence" width="337" height="147" /></a><p class="wp-caption-text">L&#39;arborescence de la commande &quot;Ouvrir Powershell&quot;</p></div>
<p>Pour ceux qui se demanderaient d&#8217;où sort la valeur &#8220;{C87DA2BC-C93B-439a-8568-88E93DFD2AB1}&#8221;, il s&#8217;agit en fait d&#8217;un GUID que j&#8217;ai généré arbitrairement. Vous pouvez prendre n&#8217;importe-quelle autre valeur ici, mais assurez-vous qu&#8217;elle <strong>ne corresponde pas déjà à un type connu</strong> sur le système ! Pour ceux qui ont installé Visual Studio, vous pouvez utiliser l&#8217;outil &#8220;GUID generator&#8221; pour le générer. Pour les autres, vous pouvez vous rendre sur <a href="http://www.guidgenerator.com/online-guid-generator.aspx">ce site</a> (n&#8217;oubliez pas d&#8217;ajouter les accolades).</p>
<p>La clé &#8220;{C87DA2BC-C93B-439a-8568-88E93DFD2AB1}&#8221; doit contenir les valeurs suivantes :</p>
<div id="attachment_158" style="width: 613px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/values.png"><img class="size-full wp-image-158" title="Valeurs" src="http://blog.freelan.org/wp-content/uploads/2010/12/values.png" alt="Valeurs" width="603" height="103" /></a><p class="wp-caption-text">Les valeurs de la clé</p></div>
<p>Les différentes clés ont les significations suivantes :</p>
<ul>
<li>La valeur &#8220;par défaut&#8221; contient le nom système que vous choisissez de donner à votre bouton d&#8217;action.</li>
<li>La propriété &#8220;Icon&#8221; contient le chemin vers la ressource &#8220;icône&#8221; à associer au bouton.</li>
<li>La propriété &#8220;InfoTip&#8221; contient le texte qui s&#8217;affiche lorsque l&#8217;on survole le bouton avec le curseur.</li>
<li>Enfin, la propriété &#8220;Title&#8221; contient le texte affiché sur le bouton.</li>
</ul>
<p>Pour terminer, indiquez la commande à exécuter en modifiant la valeur par défaut de la sous-clé &#8220;command&#8221; :</p>
<div id="attachment_159" style="width: 803px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/command.png"><img class="size-full wp-image-159" title="Commande" src="http://blog.freelan.org/wp-content/uploads/2010/12/command.png" alt="Commande" width="793" height="43" /></a><p class="wp-caption-text">La commande à exécuter</p></div>
<p>Ici, nous indiquons la commande suivante :</p><pre class="crayon-plain-tag">C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -NoLogo -NoExit -Command &quot;cd %*&quot;</pre><p>Je ne rentrerai pas dans les détails de cette commande qui sont propres à Powershell, mais soulignerai uniquement la présence du paramètre &#8220;%*&#8221;.</p>
<p>Je n&#8217;ai pas trouvé de liste exhaustive indiquant la liste et la signification des différents paramètres, mais je suis arrivé à la conclusion suivante :</p>
<ul>
<li>&#8220;%*&#8221; sera remplacé dans la commande par &#8220;tous les paramètres&#8221;.</li>
<li>&#8220;%2&#8243; sera remplacé par le dossier en cours (celui dans lequel on appuie sur le bouton d&#8217;action).</li>
</ul>
<p>Je vous invite à expérimenter les différents paramètres possibles. Si vous en trouvez d&#8217;autres ou des informations sur leur signification, n&#8217;hésitez pas à commenter, et je mettrai cet article à jour en conséquence.</p>
<p>Dans tous les cas, ouvrez un navigateur (dans &#8220;C:\&#8221; par exemple) et vous devriez obtenir le résultat suivant :</p>
<div id="attachment_146" style="width: 140px" class="wp-caption aligncenter"><a href="http://blog.freelan.org/wp-content/uploads/2010/12/action-command.png"><img class="size-full wp-image-146" title="Commande personnalisée" src="http://blog.freelan.org/wp-content/uploads/2010/12/action-command.png" alt="Commande personnalisée" width="130" height="34" /></a><p class="wp-caption-text">Commande &quot;Ouvrir Powershell&quot; personnalisée</p></div>
<p><em>N.B : Il est possible que le bouton ne s&#8217;affiche pas dans certains répertoires spéciaux comme par exemple &#8220;Ma bibliothèque&#8221; sous Windows Seven. Je n&#8217;ai pas encore trouvé comment résoudre ce problème et pour être franc, je n&#8217;ai pas vraiment cherché. Si vous trouvez une solution, n&#8217;hésitez pas à vous faire connaître !<br />
</em></p>
<h1>Des possibilités multiples</h1>
<p>Il ne s&#8217;agit que d&#8217;un aperçu très bref des possibilités offertes par la personnalisation de la barre d&#8217;action. Je suis convaincu que l&#8217;on peut aller bien plus loin et que bien d&#8217;autres options se cachent ça et là.</p>
<p>En tout cas, cette petite astuce bien pratique m&#8217;a déjà servie de nombreuses fois et j&#8217;espère que cet article vous aura intéressé. Si l&#8217;idée vous tente et que vous êtes amené à le faire de nombreuses fois, vous pouvez également générer un fichier &#8220;.reg&#8221; pour éviter la saisie manuelle.</p>
<p>En tout cas, bonne personnalisation ! Et n&#8217;hésitez pas à partager vos idées !</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.freelan.org/2010/12/21/personnaliser-la-barre-daction-rapide-sous-windows-vistaseven/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
