Ours & Hippy — le blogOurs & Hippyourshippy@huoc.orgtag:blog.huoc.org,2009:atom2010-03-15T17:07:40+01:00tag:blog.huoc.org,2009:posts/46
7 recettes pour aller plus loin avec le préprocesseur C
2010-03-15T02:12:59+01:002010-03-15T17:07:40+01:00Nhat Minh Lê (rz0)
<p>Ce soir, j’ai décidé d’être paresseux, et comme beaucoup de blogueurs,
je vous sers ici un peu de précuit, un peu de réchauffé : une liste de
trucs et astuces !
</p><p>Mais pas n’importe laquelle ! Récemment, j’ai
<a class="extern" href="http://twitter.com/nhatminhle">twitté</a> sur le triste état des
ressources disponibles sur le Web français apparaissant dans des
recherches telles que « généricité C ». Il y a beaucoup trop de cours
et autres <i>tutos</i> superficiels, destinés à inculquer aux débutants
quelques bases du langage ou de la programmation. J’ai donc décidé de
réagir, à mon échelle, en publiant sur mon modeste blog des articles
sur le C <i>pour les bons</i> ! :-° Voici donc le premier : <strong>le
préprocesseur C, pour les bons !</strong>
</p><div class="Avertissement"><p>Je ne saurais être tenu responsable de l’utilisation que vous faites
des techniques décrites ici. En particulier, si vous vous faites
insulter pour <i>code illisible</i> ou quelque chose comme cela, il ne
faudra pas venir vous plaindre. :]
</p></div><p>Si en lisant ce qui suit, vous ne vous sentez pas très à l’aise, ou ne
comprenez pas quelque chose, il vous manque peut-être quelques notions
de base ; mais pas de panique, il vous suffit d’aller lire un cours
quelconque, comme par exemple <a class="extern" href="http://www.siteduzero.com/tutoriel-3-206205-le-preprocesseur.html">ce tuto dédié au préprocesseur, sur le
SdZ</a>.
</p><p>La plupart des astuces que je présente ici sont illustrées dans des
<i>frameworks</i> tels que <a class="extern" href="http://sourceforge.net/projects/cos/">COS</a>. En moins violent, et plus usité, les
en-têtes BSD <a class="extern" href="http://netbsd.gw.com/cgi-bin/man-cgi?queue">`sys/queue.h`</a> et <a class="extern" href="http://netbsd.gw.com/cgi-bin/man-cgi?tree">`sys/tree.h`</a>, dont
je reparlerai probablement bientôt, sont également de bonnes
illustrations de certaines techniques présentées ci-dessous. Citons
aussi <a class="extern" href="http://sglib.sourceforge.net/">SGLIB</a>, dans la même veine que <code>sys/queue.h</code> et <code>sys/tree.h</code>,
mais poussant le concept un peu plus loin.
</p><p>J’ai classé les astuces par ordre croissant de degré d’aliénation
requis pour accepter de les utiliser. :-° Prêts ? <i>Let’s rock!</i>
</p><h3>Générez des séquences : opérateurs <code>,</code>, <code>?:</code>, <code>&&</code> et <code>||</code>
</h3><p>On vous a toujours dit que l’opérateur <code>,</code>, c’était mal, que c’était
Le Mal, après <a href="http://blog.huoc.org/./29-goto.html">‘goto‘</a>. Mais il y a un cas où
celui-ci peut s’avérer être un allié… intéressant. Il s’agit du
contexte des macros.
</p><p>Bien souvent, vous aurez envie que votre macro se comporte le plus
possible comme une fonction, c’est-à-dire que si celle-ci renvoie
une valeur, vous pouvez l’utiliser dans une expression.
</p><p>C’est là que l’opérateur virgule (<code>,</code>) entre en jeu : il vous permet
de placer plusieurs expressions dans votre macro, et que le tout
soit réutilisable… comme une expression ! Les opérateurs <code>?:</code>,
<code>&&</code> et <code>||</code> ajoutent un peu de variété à votre éventail de
possibilités… mais rappelez-vous qu’il faut employer avec <code>&&</code> ou
<code>||</code> des opérandes à valeur entière (ou en tout cas qu’il est
possible de convertir en entier).
</p><p>Et un exemple bidon :
</p><pre><code>#define GETARG(argcptr, argvptr) (--*(argcptr), ++*(argvptr))
#define CMP(p, q) ((p) == (q) || cmpfunc((p), (q)) == 0)
</code></pre><p>Remarquez le problème des effets de bords potentiellement provoqués
par l’évaluation de <code>p</code> ou <code>q</code>, dans la seconde macro ; nous allons
y revenir…
</p><h3>Générez des structures de contrôle : utilisez la boucle <code>for</code> !
</h3><p>Mais les macros ne sont pas limitées à remplacer des fonctions,
elles peuvent également être utilisées pour créer de nouvelles
structures de contrôle.
</p><p>À la différence d’un appel de fonction, l’utilisation d’une
structure de contrôle inclut un (ou plusieurs) blocs de code. Le
schéma de base simplifié est le suivant :
</p><pre><code>BEGIN (/* ... */) {
/* ... */;
} END (/* ... */);
</code></pre><p>Il faut bien sûr remplacer <code>BEGIN</code> et <code>END</code> par des macros
appropriées.
</p><p>En utilisant des boucles <code>for</code> pour implémenter votre structure de
contrôle, vous pouvez souvent omettre la partie <code>END</code>, et ainsi
alléger l’usage de vos macros.
</p><p>Un exemple de telles constructions peut être trouvé dans
<code>sys/queue.h</code> ; dans cet exemple, on parcourt une liste
(implémentation typique : une boucle <code>for</code>) :
</p><pre><code>LIST_FOREACH (var, head, next) {
/* 'var' pointe successivement sur chaque élément. */
/* ... */;
}
</code></pre><p>Un autre exemple, de structure plus complète, munie du marqueur de
fin, peut être trouvé dans les <a class="extern" href="http://plan9.bell-labs.com/magic/man2html/2/arg">bibliothèques standards de
Plan 9</a> ; le code suivant permet de traiter les options de
la ligne de commandes :
</p><pre><code>ARGBEGIN {
case 'a':
/* Option '-a' spécifiée. */
/* ... */;
break;
case 'b':
/* ... */;
break;
default:
usage();
} ARGEND;
</code></pre><h3>Utilisez la concaténation pour simuler la généricité par nom
</h3><p>La concaténation est un mécanisme puissant puisqu’elle permet de
générer des identificateurs à partir de fragments, dont certains
peuvent être passés en paramètres à vos macros.
</p><p>Moyennant le respect de quelques conventions dans les noms, vous
pouvez écrire du code générique, dont les parties spécifiques sont
masquées derrière un espace de nom improvisé passé en argument.
</p><p>Et encore un exemple bidon pour illustrer le principe de base :
</p><pre><code>#define RELEASE(name, p) do { \
if (name##_decrref(p) == 0) \
free(p); \
} while (/* CONSTCOND */ 0)
</code></pre><h3>Générez des définitions
</h3><p>Rappelez-vous l’histoire des effets de bord que nous avons
rencontrée plus haut. Il n’y a pas de méthode magique, pour éviter
les effets de bords, il faut passer par des variables
intermédiaires.
</p><p>Une solution simple et plutôt élégante pour résoudre ce problème est
de générer non plus du code directement, mais des fonctions… Mais,
mais, me direz-vous, quel intérêt de passer par des macros pour
générer des fonctions ? Pourquoi ne pas écrire les fonctions
directement ?
</p><p>Et bien, cette astuce, combinée à la précédente, permet un peu de
généricité ! Et un ptit exemple pas très utile, pour la route :
</p><pre><code>#define ARRAY_FIND_PROTOTYPE(name, type, cmp) \
type *name##_ARRAY_FIND(type *, size_t, type *)
#define ARRAY_FIND_GENERATE(name, type, cmp) \
type *name##_ARRAY_FIND(type *_a, size_t _n, type *_elm) \
{ \
for (; _n > 0; ++_a, --_n) { \
if (cmp(_a, _elm) == 0) \
return _a; \
} \
return NULL; \
}
#define ARRAY_FIND(name, a, n, elm) \
name##_ARRAY_FIND((a), (n), (elm))
</code></pre><p>Pour des exemples plus complets, je vous invite à jeter un coup d’œil
à l’<a class="extern" href="http://cvsweb.netbsd.org/bsdweb.cgi/src/sys/sys/tree.h">implémentation de `sys/tree.h`</a> (ou localement, dans
<code>/usr/include/sys/tree.h</code> si vous avez un BSD sous la main).
</p><p>Côté performances, vous n’avez guère de soucis à vous faire. Si vous
définissez des fonctions statiques, le compilateur s’occupera tout
seul comme un grand de les machiner comme il se doit. Et si vous
voulez lui forcer un peu la main, <code>inline</code> est là pour ça.<sup>α</sup>
</p><div class="Notes"><p>α : Mais c’est du C99… je reviendrai probablement sur cette
question dans un prochain article.
</p></div><h3>Simulez les alternatives avec la concaténation
</h3><p>Nous arrivons ainsi à la dernière astuce accessible avec un
préprocesseur à la norme C89.
</p><p>Vous connaissez certainement les directives <code>#if</code>, <code>#ifdef</code>, et
compagnie. Hélas, elles ne peuvent être utilisées à l’intérieur
d’une définition de macro.
</p><p>Dans les cas simples — mais très courants — où vous souhaitez
simplement discriminer entre plusieurs valeurs d’une constante
passée en paramètre, cependant, vous pouvez vous en sortir en
utilisant… la concaténation ! Encore elle !
</p><p>En effet, il suffit pour cela de définir chaque alternative comme
une macro séparée, nommée avec un préfixe commun, et un suffixe
dépendant du cas. Illustration :
</p><pre><code>#define DECL_STATIC_0
#define DECL_STATIC_1 static
#define DECL_STATIC(s) DECL_STATIC_##s
#define SOME_FUNCTION_PROTOTYPE(name, type, s) \
DECL_STATIC(s) type some_function(type);
</code></pre><h3>Simulez les opérations sur les n-uplets avec les macros <i>variadic</i> (C99)
</h3><p>Avec C99 ont été introduites les macros <i>variadic</i>, c’est-à-dire
acceptant un nombre variable d’arguments. Cet ajout, a priori
mineur, est en vérité très important, car il permet la manipulation
des n-uplets, soit des collections de valeurs.
</p><p>Pour nous, un n-uplet sera une suite d’éléments séparés par des
virgules, entre parenthèses. Par exemple :
</p><pre><code>(a, b, c)
</code></pre><p>Pour opérer sur des tuples, le truc de base à remarquer est le
suivant :
</p><ul><li><p>les appels de macros respectent l’équilibrage des parenthèses (ce
qui permet effectivement de passer des n-uplets comme simples
arguments à des macros) ;
</p></li><li><p>les macros sont développées autant de fois que possibles : si le
texte substitué par une macro contient encore des invocations de
macros, celles-ci sont traitées (sauf si elles ont déjà été
appelées) ;
</p></li><li><p>en <em>juxtaposant un n-uplet à un nom de macro</em>, on obtient un appel
de la macro correspondante, avec en guise d’arguments les éléments
du n-uplet !
</p></li></ul><p>Il faudrait un billet entier pour explorer en profondeur toutes les
possibilités offertes par cette astuce. Je ne vais présenter ici
qu’un cas d’utilisation simple, mais sachez qu’il est possible, par
exemple de créer une macro qui substitue tout n-uplet par 1 et toute
autre construction par 0, par exemple ! Je vous laisse imaginer ce
que l’on peut en faire, combinée aux alternatives par concaténation
expliquées ci-dessus.
</p><p>Mais revenons à notre modeste exemple, qui illustre la fonction
identité de manière ridiculement complexe :
</p><pre><code>#define IDENTITY(...) __VA_ARGS__
/*
* 'IDENTITY' peut en fait servir à supprimer des parenthèses
* superflues gênantes.
*/
#define CALL_WITH_RESOURCE(name, var, aargs, f, args) do { \
name##_type var = name##_alloc aargs; \
f(var, IDENTITY args); \
name##_free(var); \
} while (/* CONSTCOND */ 0)
#define FILE_type FILE *
#define FILE_alloc fopen
#define FILE_free fclose
/* ... */
CALL_WITH_RESOURCE (FILE, fp, ("log.txt", "a"),
fprintf, ("%d\n", 42));
</code></pre><p>COS contient une bibliothèque entière de macros travaillant sur les
n-uplets. Je vous invite à regarder le code source si cela vous
intéresse ; il s’agit plus particulièrement du dossier
<code>CosBase/include/cos/cpp/</code>, dans l’archive.
</p><h3>Énumérez les cas et déroulez les appels pour émuler la récursion (C99)
</h3><p>Au vu de ce que nous venons de voir, vous êtes en droit de vous
demander : « Peut-on faire pire ? » Et la réponse est <i>oui</i> ! :)
J’ai gardé cette astuce pour la fin car elle constitue, à mes yeux,
une limite que je ne souhaite pas, à titre personnel, franchir.
</p><p>Un peu plus haut, j’ai dit quelque chose d’important : dans une
chaîne de substitutions de macros, une macro déjà substituée ne le
sera plus, même si elle apparaît dans le texte produit… il est
donc impossible de faire des macros récursives !
</p><p>L’astuce ici consiste à dire : « L’univers est fini, je vais décrire
tout l’univers ! » Aidée de la concaténation, les possibilités sont
vraiment (in)finies ! Mais je ne m’étendrai pas davantage sur le
sujet. Encore une fois, COS contient toute une panoplie d’exemples,
qui, je n’en doute pas, apparaîtront brillants pour certains, et
affligeants pour d’autres.
</p><p>Voilà, en espérant vous avoir appris quelques petits trucs rigolos !
<i>Have fun!</i>
</p>
tag:blog.huoc.org,2009:bluestorm/9
Cohérence des effets de bord
2010-01-22T22:59:16+01:002010-01-22T22:59:16+01:00Gabriel Scherer (bluestorm)
<p>Ce week-end, je me suis promis que je rédigerai, pour le prochain
article que je lirai en entier, un court résumé de deux ou trois
paragraphes, pas plus, pour cesser d’écrire des réactions à la
longueur démesurée. Voici le résultat.
</p><p><a class="extern" href="http://coherence-lang.org/Onward09.pdf">Coherent Reaction</a> (pdf, 8 pages) est un papier de Jonathan
Edwards (pas le prédicateur américain), un type intéressant dont le
blog s’appelle <a class="extern" href="http://alarmingdevelopment.org/">Alarming Development (Dispatches from the Programmer
Liberation Front)</a>. C’est aussi le créateur du langage de
programmation visuelle <a class="extern" href="http://subtextual.org/">Subtext</a>.
</p><h3>Problématique
</h3><p>En travaillant sur Subtext, il s’est rendu compte que l’ordonnancement
des effets de bords pose problème dans un programme interactif (avec
une interface utilisateur, comme par exemple des formulaires Web). Son
exemple, dans un cadre <a class="extern" href="http://fr.wikipedia.org/wiki/Mod%C3%A8le-Vue-Contr%C3%B4leur">MVC</a> classique, est le suivant : imaginez
qu’on travaille sur trois valeurs, qui pour avoir du sens doivent
vérifier une contrainte de correction commune (par exemple la
troisième, 5, doit être la somme des deux premières, 2 et 3). C’est le
contrôleur qui vérifie ces contraintes : il dispose pour chaque valeur
d’une fonction de vérification.
</p><p>Maintenant, si on change deux de ces valeurs en même temps (la
première à 4 et la troisième à 7), il y a risque de "fausse erreur" si
les vérifications ne sont pas faites dans le bon ordre : si on modifie
la première valeur à 4, puis on vérifie, on a une erreur (la
contrainte n’est plus vérifiée, car 4 + 3 ne vaut pas 5) alors que si
on avait modifié les deux ensemble tout se serait bien passé.
</p><p>Il y a aussi des situations ou l’ordre de modification est important :
fondamentalement, on voudrait que chaque modification s’effectue en
parallèle, indépendamment, mais les langages impératifs effectuent les
effets les uns après les autres, donc il y a toujours un risque
d’interférence.
</p><p>Les solutions classiques proposent que le modèle donne plus
d’informations sur les contraintes liées au données : quelles sont les
données liées, par exemple, pour qu’on les modifie toujours toutes en
même temps au lieu d’une par une. Leur inconvénient, d’après
l’article, est une perte en modularité, puisque cela révèle une partie
de l’implémentation du modèle.
</p><h3>Coherent Reaction
</h3><p>Pour Edwards, le problème de fond est la difficulté à coordonner les
effets de bords dans les langages impératifs classiques : plus les
contraintes sont complexes, plus elles sont difficiles à gérer et
donnent lieu facilement à des erreurs de programmation difficile
à repérer. Il propose de libérer le programmeur des questions de
coordination avec sa notion de <em>cohérence</em>, qui est une forme
d’atomicité : le programmeur ne précise pas dans quel ordre faire les
modifications, et c’est le programme qui le découvre par
essais/erreurs : si des modifications produisent un état inconsistent
ou introduisent des interférences, on les annule (<i>rollback</i>) pour les
recommencer dans un ordre différent.
</p><p>Il a aussi conçu, comme expérience pratique, un langage de
programmation basé sur cette idée. C’est un langage dynamique centré
sur les données (plutôt que les comportements, comme la POO), mettant
en avant deux concepts :
</p><dl><dt>dérivation</dt><dd><p>une façon de déduire des champs du reste de l’objet : on peut dire
par exemple qu’un champ <code>c</code> dérive de la somme de deux champs <code>a</code> et
<code>b</code>. Si l’un de ces champs est mis à jour, le champ <code>c</code> sera modifié
en conséquence, pour maintenir cet invariant.
</p></dd><dt>réaction</dt><dd><p>propagation de modifications dans le sens inverse de la dérivation :
une réaction est une sorte de <i>callback</i> qui est exécuté quand le
champ auquel elle se rattache est modifié
</p></dd></dl><p>Ces deux mécanismes de transmission des modifications permettent de
maintenir simplement l’état des données; pour les combiner, c’est le
programme qui se charge de trouver un ordre des dérivations/réactions
qui évite les interférences.
</p><h3>Remarques personnelles
</h3><p>J’ai apprécié cet article, mais peut-être un peu déçu : pour la
réinvention des effets de bord, je m’attendais à quelque chose de plus
profond. La discussion des travaux similaires, étendue dans la
<a class="extern" href="http://dspace.mit.edu/handle/1721.1/45563">version longue</a> du papier, est très intéressante.
</p><p>Je ne suis pas tout à fait convaincu que cette idée peut être reprise
dans le cadre d’un langage généraliste, mais elle pourrait être
appropriée comme paradigme spécialisé des parties interactives d’un
programme. La comparaison avec la programmation fonctionnelle réactive
serait certainement intéressante, mais c’est un sujet que je ne
connais pas (bien que j’envisage de m’y intéresser prochainement) donc
je ne pourrais pas en dire plus.
</p><p>Enfin, l’article se termine quasiment par un paragraphe, en réaction
à une citation très classique d’Alan Kay, qui me semble tout à fait
obscur. Je ne pense pas le comprendre, c’est bien trop
flou/littéraire/psychologique pour moi, mais je pense qu’il pourrait
intéresser certains lecteurs :
</p><blockquote><div><blockquote><div>Smalltalk’s design—and existence—is due to the in-
sight that everything we can describe can be repre-
sented by the recursive composition of a single kind
of behavioral building block that hides its combina-
tion of state and process inside itself and can be dealt
with only through the exchange of messages.
<dl><dt>– Alan Kay : The early history of smalltalk</dt><dd></dd></dl></div></blockquote><p>The conceptual model of Coherence is in a sense opposite to that of
Object Oriented languages. As Alan Kay’s quote above indicates, the
central metaphor of OO is that of mes- saging: written
communication. The central metaphor of Co- herence is that of
observing a structure and directly manip- ulating it. These two
metaphors map directly onto the two primary mechanisms of the mind:
language and vision.
</p></div></blockquote>