tag:linuxfr.org,2005:/tags/pointeur/publicLinuxFr.org : les contenus étiquetés avec « pointeur »2019-03-13T12:28:25+01:00/favicon.pngtag:linuxfr.org,2005:Post/399672019-03-13T12:28:25+01:002019-03-14T00:54:44+01:00Passer un pointeur de membre de classe à une fonction statique ou une lambda sans capture?<p>Bonjour.</p>
<p>Je suis en train de me faire la main sur l'interfaçage de Lua avec le C++. J'ai compris qu'il existe une tripotée de bibliothèques pour faire ça mais je voudrais profiter de l'occasion pour affûter ma pratique des patrons en C++ sur la résolution d'un casse-tête (cherchez pas, j'aime bien ça).</p>
<h2 id="toc-le-contexte">Le contexte</h2>
<p>L'idée que je poursuis est de passer un pointeur vers un membre d'une classe quelconque à une fonction statique ou une lambda (mais celle-ci doit être sans capture). J'ai défini une classe C++ qui sert de modèle pour le passage d'objets de type user data. Les fonctions Lua que j'ai définies récupèrent un pointeur vers l'instance en vue d'appeler une fonction membre de cette instance.</p>
<p>Voici un exemple de classe C++:</p>
<pre><code class="c++"><span class="c1">// Classe C++ à utiliser en tant que user_data</span>
<span class="k">class</span> <span class="nc">person</span>
<span class="p">{</span>
<span class="k">private</span><span class="o">:</span>
<span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">string</span> <span class="n">name</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">age</span><span class="p">;</span>
<span class="k">public</span><span class="o">:</span>
<span class="n">person</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">n</span><span class="p">)</span> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">n</span><span class="p">)</span> <span class="p">{}</span>
<span class="n">person</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">n</span><span class="p">,</span> <span class="kt">int</span> <span class="n">a</span><span class="p">)</span> <span class="o">:</span> <span class="n">name</span><span class="p">(</span><span class="n">n</span><span class="p">),</span> <span class="n">age</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="p">{}</span>
<span class="o">~</span><span class="n">person</span><span class="p">()</span> <span class="p">{</span> <span class="n">fprintf</span><span class="p">(</span><span class="n">stderr</span><span class="p">,</span> <span class="s">"Destroying person instance %p ...</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="k">this</span><span class="p">);</span> <span class="p">}</span>
<span class="kt">void</span> <span class="n">print</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"%s is %i</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">name</span><span class="p">.</span><span class="n">c_str</span><span class="p">(),</span> <span class="n">age</span><span class="p">);</span> <span class="p">}</span>
<span class="kt">int</span> <span class="n">getAge</span><span class="p">()</span> <span class="k">const</span> <span class="p">{</span> <span class="k">return</span> <span class="n">age</span><span class="p">;</span> <span class="p">}</span>
<span class="kt">void</span> <span class="n">setAge</span><span class="p">(</span><span class="kt">int</span> <span class="n">a</span><span class="p">)</span> <span class="p">{</span> <span class="n">age</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span> <span class="p">}</span>
<span class="p">};</span></code></pre>
<p>Les trois méthodes que je publie dans Lua sont "print", "getAge" et "setAge". Ces trois méthodes sont appelées depuis des fonctions (à la signature) Lua enregistrées via <a href="http://pgl.yoyo.org/luai/i/luaL_register">luaL_register()</a>, qui demande un tableau d'éléments de type luaL_Reg.</p>
<p>J'ai défini un patron qui marche très bien, qui récupère le pointeur vers l'instance de la classe depuis la pile, appelle la fonction en prenant chacun de ses arguments dans la pile et retourne le résultat dans la pile.</p>
<p>Mon casse-tête est la manière de déclarer mon tableau d'éléments <a href="http://pgl.yoyo.org/luai/i/luaL_Reg">luaL_Reg</a>:</p>
<pre><code class="c++"><span class="c1">/// Ugly way to create a list of registered functions... (waiting for better)</span>
<span class="cp">#define lua_reg(name, func) \</span>
<span class="cp"> luaL_Reg{name, [](lua::stateptr_t L) { return lua::forward(L, (func)); } }</span>
<span class="p">...</span>
<span class="c1">// Création d'un type user_data pour la classe person:</span>
<span class="n">Lua</span><span class="p">.</span><span class="n">user_type</span><span class="o"><</span><span class="n">person</span><span class="p">,</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span><span class="p">,</span> <span class="kt">int</span><span class="o">></span><span class="p">(</span>
<span class="s">"Person"</span><span class="p">,</span>
<span class="n">lua_reg</span><span class="p">(</span><span class="s">"print"</span><span class="p">,</span> <span class="o">&</span><span class="n">person</span><span class="o">::</span><span class="n">print</span><span class="p">),</span>
<span class="n">lua_reg</span><span class="p">(</span><span class="s">"getAge"</span><span class="p">,</span> <span class="o">&</span><span class="n">person</span><span class="o">::</span><span class="n">getAge</span><span class="p">),</span>
<span class="n">lua_reg</span><span class="p">(</span><span class="s">"setAge"</span><span class="p">,</span> <span class="o">&</span><span class="n">person</span><span class="o">::</span><span class="n">setAge</span><span class="p">)</span>
<span class="p">);</span></code></pre>
<p>Dans cet exemple je me sers d'une fonction lambda comme fonction inscrite. J'aurais pu me servir d'une fonction statique mais ça ne change pas le casse-tête.</p>
<h2 id="toc-le-casse-tête">Le casse-tête</h2>
<p>Je voudrais faire ça:</p>
<pre><code class="c++"><span class="k">template</span> <span class="o"><</span><span class="cm">/* n'importe quoi de possible */</span><span class="o">></span>
<span class="n">luaL_Reg</span> <span class="n">make_reg</span><span class="p">(</span><span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="n">name</span><span class="p">,</span> <span class="cm">/* quelque chose qui désigne une fonction membre de person*/</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">{</span>
<span class="n">name</span><span class="p">,</span>
<span class="p">[</span> <span class="cm">/* une lambda sans capture */</span><span class="p">](</span><span class="n">lua_State</span><span class="o">*</span> <span class="n">L</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">lua</span><span class="o">::</span><span class="n">forward</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="cm">/* la fonction membre */</span><span class="p">);</span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>Le casse-tête est ceci:</p>
<ul>
<li><p>je peux passer un pointeur vers une fonction membre en tant que paramètre à une fonction et profiter de la déduction automatique des types/arguments, puis utiliser une lambda mais alors celle-ci doit capturer la valeur du paramètre… ce qui donne une signature incompatible avec le type lua_cfunction.</p></li>
<li><p>je peux utiliser un argument de patron pour passer le pointeur vers la fonction membre et ça marche très bien… sauf que c'est plutôt casse-bonbons. Exemple:</p></li>
</ul>
<pre><code class="c++"><span class="k">template</span> <span class="o"><</span><span class="k">class</span> <span class="nc">U</span><span class="o">></span>
<span class="k">struct</span> <span class="n">forwarder</span>
<span class="p">{</span>
<span class="c1">//~ template <typename T, T (U::*function)(void) const></span>
<span class="c1">//~ static int __f(lua::stateptr_t L) { return lua::forward(L, function); }</span>
<span class="k">template</span> <span class="o"><</span><span class="k">typename</span> <span class="n">T</span><span class="p">,</span> <span class="n">T</span> <span class="n">function</span><span class="o">></span>
<span class="k">static</span> <span class="kt">int</span> <span class="n">__f</span><span class="p">(</span><span class="n">lua</span><span class="o">::</span><span class="n">stateptr_t</span> <span class="n">L</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="n">lua</span><span class="o">::</span><span class="n">forward</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">function</span><span class="p">);</span> <span class="p">}</span>
<span class="p">};</span>
<span class="n">lua_CFunction</span> <span class="n">test</span> <span class="o">=</span> <span class="n">forwarder</span><span class="o"><</span><span class="n">person</span><span class="o">>::</span><span class="n">__f</span><span class="o"><</span><span class="k">decltype</span><span class="p">(</span><span class="o">&</span><span class="n">person</span><span class="o">::</span><span class="n">print</span><span class="p">),</span> <span class="o">&</span><span class="n">person</span><span class="o">::</span><span class="n">print</span><span class="o">></span><span class="p">;</span></code></pre>
<p>Le plus simple que je sois arrivé à écrire demande de me taper un decltype(…) pour le premier argument du patron. Au final, j'appellerais bien une macro pour faire ça, ce qui est exactement ce que je souhaitais éviter et qui m'a conduit à écrire ce post.</p>
<p>Ce casse-tête n'est pas juste lié à Lua, j'aurais pu tomber dessus à n'importe quelle occasion. C'est juste que mes expérimentations avec Lua/C++ m'y ont amené plus tôt que prévu.</p>
<p>À votre avis, y a-t-il une solution élégante à ce casse-tête?</p>
<div><a href="https://linuxfr.org/forums/programmation-c/posts/passer-un-pointeur-de-membre-de-classe-a-une-fonction-statique-ou-une-lambda-sans-capture.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/116670/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/forums/programmation-c/posts/passer-un-pointeur-de-membre-de-classe-a-une-fonction-statique-ou-une-lambda-sans-capture#comments">ouvrir dans le navigateur</a>
</p>
FantastIXhttps://linuxfr.org/nodes/116670/comments.atomtag:linuxfr.org,2005:Post/396662018-11-25T19:01:26+01:002018-11-25T19:01:26+01:00Pointeur de souris, debian, mate<p>Bonjour à tous.</p>
<p>J'ai un petit problème avec le pointeur de ma souris.<br>
J'ai fait l'erreur d'avoir installé d'autres environnements de bureau pour tester (openbox, lxde), et cela a un peu mis le bazar sur mon système, applications en double dans mon menu, etc. Le seul truc que je n'arrive pas à régler est l'apparence du pointeur de souris, sur certaines fenêtres ma dernière configuration via l'apparence des préférences est bien pris en compte, sur d'autres c'est un autre type de pointeur qui est utilisé et le pointeur ⬉ n'est pas respecté, ce qui est marrant c'est que le pointeur sur le bureau est celui par défaut (le noir moche), si je fais une capture d'écran du bureau c'est un autre pointeur, bref tout merdouille selon le gestionnaire de fenêtre utilisé par les applications.</p>
<p>Mon environnement de bureau est mate avec le gestionnaire de fenêtre par défaut.</p>
<p>Donc je cherche un moyen de réinitialiser cela afin que ce que je définis dans l'apparence système soit bien respecté.</p>
<p>Merci d'avance à ceux pouvant m'aiguiller vers une solution. </p>
<div><a href="https://linuxfr.org/forums/linux-debian-ubuntu/posts/pointeur-de-souris-debian-mate.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/115812/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/forums/linux-debian-ubuntu/posts/pointeur-de-souris-debian-mate#comments">ouvrir dans le navigateur</a>
</p>
gledhttps://linuxfr.org/nodes/115812/comments.atomtag:linuxfr.org,2005:Post/380852017-04-21T12:21:11+02:002017-04-21T12:21:11+02:00classe C++, membre structure C et allocation dynamique<p>Bonjour,<br>
je m'essaye (débutant) à développer une classe c++ dont certains membre sont des pointeurs vers des structures définies dans une bibliothèque c.<br>
Par exemple :</p>
<pre><code class="C++"><span class="cm">/* maclasse.hh */</span>
<span class="cp">#include</span> <span class="cpf"><une_lib_en_c.h></span><span class="cp"></span>
<span class="k">class</span> <span class="nc">MaClasse</span> <span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="n">MaClasse</span><span class="p">();</span>
<span class="o">~</span><span class="n">MaClasse</span><span class="p">();</span>
<span class="kt">int</span> <span class="nf">init</span><span class="p">();</span>
<span class="k">private</span><span class="o">:</span>
<span class="n">ma_struct_c</span> <span class="o">*</span><span class="n">s</span><span class="p">;</span> <span class="c1">//struct définie dans une_lib_en_c.h</span>
<span class="p">};</span>
<span class="n">MaClasse</span><span class="o">::</span><span class="n">MaClasse</span><span class="p">()</span> <span class="p">{</span>
<span class="n">s</span> <span class="o">=</span> <span class="k">nullptr</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">MaClasse</span><span class="o">::~</span><span class="n">MaClasse</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// s_destroy définie dans une_lib_en_c.h </span>
<span class="n">s_destroy</span><span class="p">(</span><span class="o">&</span><span class="n">s</span><span class="p">);</span>
<span class="k">delete</span> <span class="n">s</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Comme il semble que ce n'est pas possible/propre de vérifier</span>
<span class="c1">// la bonne exécution de l'allocation dynamique de s dans le constructeur,</span>
<span class="c1">// fonction d'initialisation avec éventuelle erreur en retour.</span>
<span class="kt">int</span> <span class="n">MaClasse</span><span class="o">::</span><span class="n">init</span><span class="p">()</span> <span class="p">{</span>
<span class="n">s</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ma_struct_c</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">!=</span> <span class="k">nullptr</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// s_init définie dans une_lib_en_c.h</span>
<span class="c1">// retourne 0 en cas de succès.</span>
<span class="k">return</span> <span class="n">s_init</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span></code></pre>
<p>Quelques questions :</p>
<p>Est-ce que c'est propre/utile/important de remplacer les malloc/free par des new/delete ?</p>
<p>Est-ce que cette implémentation est correcte pour la bonne libération de la mémoire allouée dynamiquement dans <code>MaClasse::init()</code> lorsque le destructeur de MaClasse est appelé ?</p>
<p>Bonus : Puisque j'ai au moins un pointeur comme variable membre, je suppose que je dois faire gaffe à redéfinir le constructeur de copie de MaClasse ?</p>
<p>Merci de vos conseils ou suggestions.</p><div><a href="https://linuxfr.org/forums/programmation-c/posts/classe-c-membre-structure-c-et-allocation-dynamique.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/111743/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/forums/programmation-c/posts/classe-c-membre-structure-c-et-allocation-dynamique#comments">ouvrir dans le navigateur</a>
</p>
audionumahttps://linuxfr.org/nodes/111743/comments.atomtag:linuxfr.org,2005:Post/338852014-04-17T13:04:10+02:002014-04-18T23:13:50+02:00Pointeur Laser Vert - Technique et Législation Française ?<p>NdM : spam pour placer un lien publicitaire sur des lasers sous prétexte de question sur la législation en la matière, par un compte sans autre commentaire/contenu et avec une adresse de spammeur.</p><div><a href="https://linuxfr.org/forums/general-general/posts/pointeur-laser-vert-technique-et-legislation-francaise.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/101911/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/forums/general-general/posts/pointeur-laser-vert-technique-et-legislation-francaise#comments">ouvrir dans le navigateur</a>
</p>
hiboomohttps://linuxfr.org/nodes/101911/comments.atomtag:linuxfr.org,2005:Diary/345502013-12-04T11:10:51+01:002013-12-04T14:22:43+01:00Spam spam spamLicence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>NdM : spam pour publier 3 liens vers le site du spammeur</p><div><a href="https://linuxfr.org/users/apple23/journaux/spam-spam-spam.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/100566/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/apple23/journaux/spam-spam-spam#comments">ouvrir dans le navigateur</a>
</p>
apple23https://linuxfr.org/nodes/100566/comments.atomtag:linuxfr.org,2005:Diary/337432013-03-14T23:58:37+01:002013-03-14T23:58:37+01:00PyAlsaCap : Python, pointeurs, et cartes sons…Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<h2 id="sommaire">Sommaire</h2>
<ul><li>
<a href="#toc_0">Python et ctypes</a>
</li>
<li>
<a href="#toc_1">Passage par référence</a>
</li>
<li>
<a href="#toc_2">C'était trop facile</a>
</li>
<li>
<a href="#toc_3">Et là c'est le drame</a>
</li>
<li>
<a href="#toc_4">Je veux mes allocs !</a>
</li>
<li>
<a href="#toc_5">Récupérer les valeurs</a>
</li>
<li>
<a href="#toc_6">Headers et malloc en Python ?</a>
</li>
<li>
<a href="#toc_7">PyAlsaCap</a>
</li>
<li>
<a href="#toc_8">Le code</a>
</li>
<li>
<a href="#toc_9">Conclusion</a>
</li>
</ul><p>Pour fêter le retour de <a href="http://linuxfr.org">DLFP</a> après cette trop longue <em>vacance</em>, voici un petit journal pythonesque, mais pas seulement !</p>
<p>Dans mon <a href="http://linuxfr.org/users/illwieckz/journaux/exposer-un-ou-des-modules-python-sur-d-bus-proof-of-concept">dernier journal</a>, nous avions joué avec l'introspection Python et l'export de fonction sur D-Bus. Pour ce faire, nous avions généré du Python avec Python !</p>
<p>Cette fois-ci, je propose de combiner le meilleur de Python avec le meilleur de C ! Nous allons jouer avec des pointeurs… depuis un interpréteur Python !</p>
<p>En plus de tout cela, nous ne sommes plus limité à Python 2. Et nous pouvons sortir avec une peau toute neuve, laissant la vieille mue derrière nous !</p>
<p>Pour cela il faut remercier lolop< qui a partagé une <a href="http://linuxfr.org/users/illwieckz/journaux/exposer-un-ou-des-modules-python-sur-d-bus-proof-of-concept#comment-1434431">excellente idée</a> : attaquer <code>libasound</code> directement depuis Python avec le module <a href="http://docs.python.org/3.3/library/ctypes.html">ctype</a>. Si vous ne l'aviez pas encore pertinenté, c'est le moment !</p>
<p>Mais cela n'est pas tout, il faut aussi remercier tankey< de nous avoir fait découvrir <a href="http://www.volkerschatz.com/noise/alsa.html#alsacap"><code>alsacap</code></a> !</p>
<p><code>alsacap</code> est un merveilleux petit programme développé par Volker Schatz. Je ne sais pas pourquoi ce programme ne fait pas partie de <a href="http://alsa.opensrc.org/Alsa-utils">alsa-utils</a>, parce que tout en étant un petit bijou, il répond à une problématique légitime et pourtant insatisfaite : décrire le matériel pris en charge par ALSA ! Cela peut paraitre absurde, mais ces informations ne sont accessibles qu'aux développeurs, ALSA ne fournit pas d'interface utilisateur potable pour obtenir ces informations depuis le <em>shell</em>.</p>
<p>Si vous n'avez pas pertinenté tankey< pour cette contribution de grande valeur, vous pouvez le faire en suivant le lien suivant :<br /><a href="http://linuxfr.org/users/illwieckz/journaux/exposer-un-ou-des-modules-python-sur-d-bus-proof-of-concept#comment-1434639">lien suivant</a>. ;)</p>
<p>Ce journal est une grande avancé dans la quête que je poursuis : connaître les noms de mes cartes sons, les échantillonnages pris en charge, le nombre de canaux audio et autres informations très utiles, et ce depuis Python ! Nous allons réécrire <code>alsacap</code> en Python !</p>
<p>Le serpent est rusé, dit-on… et moi j'ai chuté !</p>
<h2 id="toc_0">Python et ctypes</h2>
<p>Ctypes est donc une bibliothèque Python qui fournit des <em>types</em> compatibles C et qui permet d'appeler des fonctions appartenant à des bibliothèques C… Avec, on peut donc interfacer C et Python sans compilation !</p>
<p>Pour utiliser une bibliothèque C, rien de plus simple, essayons avec la libc :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">ctypes</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">libc</span> <span class="o">=</span> <span class="n">cdll</span><span class="o">.</span><span class="n">LoadLibrary</span><span class="p">(</span><span class="s">"libc.so.6"</span><span class="p">)</span>
</code>
</pre>
<p>Désormais, nous voilà avec un objet nommé <code>libc</code>, les fonctions sont tout simplement des éléments de cet objet. Malheureusement, l'introspection ne marche pas sur ce type d'objet, il nous faudra donc, pour connaître les fonctions, nous référer à leur documentation officielle ou bien au code source (vive le logiciel libre ) !</p>
<p>Ainsi, pour écrire sur la sortie standard le caractère « a », nous pouvons appeler la fonction <code>putchar</code> ainsi :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="n">libc</span><span class="o">.</span><span class="n">putchar</span><span class="p">(</span><span class="n">c_char</span><span class="p">(</span><span class="n">b</span><span class="s">'a'</span><span class="p">))</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="mi">97</span>
<span class="n">a</span>
</code>
</pre>
<p>Notre caractère « a » est bien affiché !</p>
<p>Nous découvrons une chose, le type <code>c_char</code>. Le module <code>ctypes</code> fournit une fournée de types de ce type (<code>c_int</code>, <code>c_bool</code>…) qui peuvent être convertis vers et depuis les types Python (<code>int</code>, <code>bool</code>).</p>
<p>Nous découvrons autre chose : le code de retour est numérique, en effet, c'est un entier.<br />
Nous savons que <code>putchar</code> renvoie, si succès, la valeur numérique du caractère passé en paramètre. Mais il existe des fonctions qui renvoient d'autres types ! Et bien nous pouvons spécifier le type attendu avec l'attribut <code>restype</code>. Ce n'est pas très utile, mais nous allons le faire pour <code>putchar</code>, nous allons déclarer que le type de retour est <code>c_char</code>.</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="n">libc</span><span class="o">.</span><span class="n">putchar</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="n">libc</span><span class="o">.</span><span class="n">putchar</span><span class="p">(</span><span class="n">c_char</span><span class="p">(</span><span class="n">b</span><span class="s">'a'</span><span class="p">))</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="n">b</span><span class="s">'a'</span>
<span class="n">a</span>
</code>
</pre>
<p>Avant de passer aux choses plus intéressantes, attardons-nous un instant pour adresser une parole pleine de bonnes intentions à nos amis du bouchot :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="n">libc</span><span class="o">.</span><span class="n">printf</span><span class="p">(</span><span class="n">c_char_p</span><span class="p">(</span><span class="n">b</span><span class="s">"salut les moules ! \_o< coincoin"</span><span class="p">))</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="mi">32</span>
<span class="n">salut</span> <span class="n">les</span> <span class="n">moules</span><span class="err"> !</span> \<span class="n">_o</span><span class="o"><</span> <span class="n">coincoin</span>
</code>
</pre>
<p>Vous aurez remarqué que le nom du type <code>c_char_p</code> termine par les deux caractères <code>_p</code>, ce qui signifie… que c'est un pointeur. Effectivement, nous savons qu'en C, les chaînes de caractères sont des tableaux de <code>char</code>, et que l'adresse du tableau est l'adresse de son premier caractère ! Avoir bon pointeur c'est s'assurer d'avoir toujours bon caractère…</p>
<h2 id="toc_1">Passage par référence</h2>
<p>Pour décrire nos cartes, il va falloir appeler des fonctions de <code>libasound</code>. Puisque <code>alsacap</code> fait tout ce que nous désirons, nous allons l'imiter. Il nous servira de guide spirituel !</p>
<p>Dans <a href="http://www.volkerschatz.com/noise/alsacap.c"><code>alsacap.c</code></a> nous trouvons très vite une fonction nommée <code>scancards</code>. Dans cette fonction on trouve le code suivant (à peu près) :</p>
<pre>
<code class="c"><span class="kt">int</span> <span class="n">card</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">snd_card_next</span><span class="p">(</span><span class="o">&</span><span class="n">card</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">return</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">card</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* some code */</span>
<span class="k">if</span> <span class="p">(</span><span class="n">snd_card_next</span><span class="p">(</span><span class="o">&</span><span class="n">card</span><span class="p">)</span> <span class="o"><</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Regardons premièrement la forme de ce code, quelque chose nous saute aux yeux. Nous voyons que la fonction <code>snd_card_next</code> prend pour argument le paramètre <code>&card</code>. Nous devrions plutôt dire que la fonction <code>snd_card_next</code> prend pour argument la référence de <code>card</code>, c'est à dire son adresse.</p>
<p>En Python avec <code>ctypes</code> nous pouvons écrire un passage par référence ainsi :</p>
<pre>
<code class="python"><span class="n">snd_card_next</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">card</span><span class="p">))</span>
</code>
</pre>
<p>Maintenant, faisons plus attention au sens de ce code, ce qu'il fait. Nous remarquons que la variable <code>card</code> est initialisée à <code>-1</code>, et qu'ensuite elle passée par référence à la fonction <code>snd_card_next</code> qui doit donc certainement changer sa valeur par celle de la carte suivante. Nous remarquons aussi que cette fonction retourne un entier négatif en cas d'erreur.</p>
<p>Transposons en python, avec un peu de code supplémentaire pour analyser le fonctionnement. Pour simplifier nous ne nous préoccupons pas des erreurs :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">ctypes</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">libasound</span> <span class="o">=</span> <span class="n">cdll</span><span class="o">.</span><span class="n">LoadLibrary</span><span class="p">(</span><span class="s">"libasound.so.2"</span><span class="p">)</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="n">card</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_card_next</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">card</span><span class="p">))</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="mi">0</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="k">while</span> <span class="n">card</span><span class="o">.</span><span class="n">value</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">:</span>
<span class="o">...</span><span class="p">:</span> <span class="k">print</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">card</span><span class="o">.</span><span class="n">value</span><span class="p">))</span>
<span class="o">...</span><span class="p">:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_card_next</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">card</span><span class="p">))</span>
<span class="o">...</span><span class="p">:</span>
<span class="mi">0</span>
<span class="mi">29</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="k">print</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">card</span><span class="o">.</span><span class="n">value</span><span class="p">))</span>
<span class="o">-</span><span class="mi">1</span>
</code>
</pre>
<p>Nous vérifions nos suppositions : la fonction <code>snd_card_next</code> donne la carte suivante à celle donnée en argument. La valeur -1 signifie que la liste a été parcourue et qu'il faut recommencer à zéro (si la première s'appelle zéro).</p>
<p>Nous pouvons donc écrire la fonction <code>GetCards</code> suivante :</p>
<pre>
<code class="python"><span class="k">def</span> <span class="nf">GetCards</span><span class="p">():</span>
<span class="n">cards</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">card</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_card_next</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">card</span><span class="p">))</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">return</span> <span class="n">cards</span>
<span class="k">while</span> <span class="n">card</span><span class="o">.</span><span class="n">value</span> <span class="o">>=</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">cards</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">card</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_card_next</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">card</span><span class="p">))</span> <span class="o"><</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">return</span> <span class="n">cards</span>
</code>
</pre><pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">8</span><span class="p">]:</span> <span class="n">GetCards</span><span class="p">()</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">8</span><span class="p">]:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">29</span><span class="p">]</span>
</code>
</pre>
<p>J'ai effectivement deux cartes sons, <code>hw:0</code> et <code>hw:29</code>. Première réussite !</p>
<h2 id="toc_2">C'était trop facile</h2>
<p>Dans <code>alsacap</code>, le code qui nous intéresse ensuite est celui-là :</p>
<pre>
<code class="c"><span class="n">snd_ctl_card_info</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">info</span><span class="p">);</span>
</code>
</pre>
<p>Nous lisons un peu plus haut que ces deux variables sont déclarés ainsi :</p>
<pre>
<code class="c"><span class="k">static</span> <span class="n">snd_ctl_t</span> <span class="o">*</span><span class="n">handle</span><span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
<span class="k">static</span> <span class="n">snd_ctl_card_info_t</span> <span class="o">*</span><span class="n">info</span><span class="p">;</span>
</code>
</pre>
<p>Oh, des pointeurs ! Mais quels sont leurs types ?</p>
<p>Nous trouvons dans <code>/usr/include/alsa/control.h</code> les déclarations suivantes :</p>
<pre>
<code class="c"><span class="cm">/** CTL handle */</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">_snd_ctl</span> <span class="n">snd_ctl_t</span><span class="p">;</span>
<span class="cm">/** CTL card info container */</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">_snd_ctl_card_info</span> <span class="n">snd_ctl_card_info_t</span><span class="p">;</span>
</code>
</pre>
<p>Ce sont des pointeurs de structures, autrement dit des pointeurs de pointeurs !<br />
Aïe aïe aïe… nous ne pourrons pas utiliser <code>byref()</code>, car nous ne connaissons pas ce qui est pointé ! Surtout nous ne savons pas trop comment déclarer ça dans Python…</p>
<p>En fait, nous ne lirons jamais directement ces structures, nous utiliserons des fonctions pour cela. Par exemple, pour obtenir le nom de la carte son, nous ferons :</p>
<pre>
<code class="c"><span class="n">snd_ctl_card_info_get_name</span><span class="p">(</span><span class="n">info</span><span class="p">))</span>
</code>
</pre>
<p>C'est rassurant, nous n'avons pas besoin de connaître exactement les structures, il semble qu'il nous faille seulement savoir passer les pointeurs de fonctions en fonctions.</p>
<p>Nous utiliserons donc le type générique <code>c_void_p</code>. Par souci de clarté, nous préfixerons aussi les noms de variables de type C avec les deux caractères <code>c_</code>.</p>
<p>En C nous avions quelque chose comme :</p>
<pre>
<code class="c"><span class="cp">#define HWCARDTEMPL "hw:%d"</span>
<span class="cp">#define HWDEVLEN 32</span>
<span class="n">card</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">snprintf</span><span class="p">(</span><span class="n">hwdev</span><span class="p">,</span> <span class="n">HWDEVLEN</span><span class="p">,</span> <span class="n">HWCARDTEMPL</span><span class="p">,</span> <span class="n">card</span><span class="p">);</span>
<span class="n">snd_ctl_open</span><span class="p">(</span><span class="o">&</span><span class="n">handle</span><span class="p">,</span> <span class="n">hwdev</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">snd_ctl_card_info</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">info</span><span class="p">)</span>
</code>
</pre>
<p>Nous allons essayer en Python :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">ctypes</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">libasound</span> <span class="o">=</span> <span class="n">cdll</span><span class="o">.</span><span class="n">LoadLibrary</span><span class="p">(</span><span class="s">"libasound.so.2"</span><span class="p">)</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="n">card</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="n">hwdev</span> <span class="o">=</span> <span class="s">"hw:"</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">card</span><span class="p">)</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="n">b_hwdev</span> <span class="o">=</span> <span class="n">create_string_buffer</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="n">hwdev</span><span class="p">))</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="n">c_handle</span> <span class="o">=</span> <span class="n">c_void_p</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">7</span><span class="p">]:</span> <span class="n">c_info</span> <span class="o">=</span> <span class="n">c_void_p</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">8</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_open</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">c_handle</span><span class="p">),</span> <span class="n">b_hwdev</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">8</span><span class="p">]:</span> <span class="mi">0</span>
</code>
</pre>
<p>Bien, ça a l'air de passer ! La valeur retournée aurait été non nulle s'il y avait eu une erreur…</p>
<p>On peut remarquer la conversion de chaîne de caractère entre <a href="http://docs.python.org/3.1/library/stdtypes.html#sequence-types-str-bytes-bytearray-list-tuple-range">les types <code>str</code> et <code>bytes</code></a>, ce sera souvent utile. Aussi, lorsque nous passons une chaîne de type <code>byte</code>, il n'est pas nécessaire de la passer via <code>byref()</code>. <code>ctypes</code> fait cela tout seul.</p>
<p>Continuons…</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info</span><span class="p">(</span><span class="n">c_handle</span><span class="p">,</span> <span class="n">c_info</span><span class="p">)</span>
<span class="n">python3</span><span class="p">:</span> <span class="n">control</span><span class="o">.</span><span class="n">c</span><span class="p">:</span><span class="mi">234</span><span class="p">:</span> <span class="n">snd_ctl_card_info</span><span class="p">:</span> <span class="n">Assertion</span> <span class="err">`</span><span class="n">ctl</span> <span class="o">&&</span> <span class="n">info</span><span class="s">' failed.</span>
<span class="n">Abandon</span>
</code>
</pre>
<p>En fait non, nous ne continuons pas ! :)</p>
<h2 id="toc_3">Et là c'est le drame</h2>
<p>Nos manipulations de pointeurs semble correctes :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="n">handle</span> <span class="o">=</span> <span class="n">c_void_p</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">7</span><span class="p">]:</span> <span class="n">info</span> <span class="o">=</span> <span class="n">c_void_p</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">8</span><span class="p">]:</span> <span class="n">handle</span><span class="o">.</span><span class="n">value</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_open</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">c_handle</span><span class="p">),</span> <span class="n">b_hwdev</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="mi">0</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">10</span><span class="p">]:</span> <span class="n">c_handle</span><span class="o">.</span><span class="n">value</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">10</span><span class="p">]:</span> <span class="mi">51737712</span>
</code>
</pre>
<p>Le pointeur <code>c_handle</code> passe bien de la valeur <code>NULL</code> à une adresse mémoire !</p>
<p>Qu'est ce qui ne va pas ? Nous avons oublié une chose essentielle…</p>
<p>Déjà, nous aurions pu remarquer que nous passions en paramètre un pointeur non initialisé, <code>c_info</code>. Nous cherchons donc où il est initialisé… </p>
<p>Nous trouvons ce code :</p>
<pre>
<code class="c"><span class="n">snd_ctl_card_info_alloca</span><span class="p">(</span><span class="o">&</span><span class="n">info</span><span class="p">);</span>
</code>
</pre>
<p>Nous avons donc trouvé ce qui semble être une fonction qui initialise l'adresse vers laquelle le pointeur pointe… Et nous découvrons que ce que cela semble être une fonction d'allocation. En effet, on peut se douter qu'à l'adresse pointée par <code>info</code>, il n'y aura pas qu'un pointeur, mais des tas de données, chaînes de caractères, listes, etc.</p>
<p>Aïe aïe aïe, pour le pointeur nommé <code>handle</code>, <a href="http://en.wikipedia.org/wiki/Handle_%28computing%29">comme son nom l'indique</a>, nous n'avions pas besoin de nous soucier de son allocation, c'est un pointeur qui contient une référence vers un objet que nous ne gérons pas nous-même. Pour le pointeur nommé <code>info</code>, c'est tout autre chose !</p>
<p>Et si nous essayons, depuis python, d'appeler la fonction <code>snd_ctl_card_info_alloca</code> ?</p>
<p>Je l'ai tenté pour vous :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">11</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_alloca</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">c_info</span><span class="p">))</span>
<span class="o">---------------------------------------------------------------------------</span>
<span class="ne">AttributeError</span> <span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">)</span>
<span class="o"><</span><span class="n">ipython</span><span class="o">-</span><span class="nb">input</span><span class="o">-</span><span class="mi">15</span><span class="o">-</span><span class="mi">13</span><span class="n">b9fd869a33</span><span class="o">></span> <span class="ow">in</span> <span class="o"><</span><span class="n">module</span><span class="o">></span><span class="p">()</span>
<span class="o">----></span> <span class="mi">1</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_alloca</span><span class="p">(</span><span class="n">byref</span><span class="p">(</span><span class="n">c_info</span><span class="p">))</span>
<span class="p">[</span><span class="err">…</span><span class="p">]</span>
<span class="ne">AttributeError</span><span class="p">:</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="o">/</span><span class="n">libasound</span><span class="o">.</span><span class="n">so</span><span class="o">.</span><span class="mi">2</span><span class="p">:</span> <span class="n">undefined</span> <span class="n">symbol</span><span class="p">:</span> <span class="n">snd_ctl_card_info_alloca</span>
</code>
</pre>
<p>C'est un cuisant échec.</p>
<h2 id="toc_4">Je veux mes allocs !</h2>
<p>Mais que se passe-t'il ?</p>
<p>Ce ne serait pas une fonction ? mais qu'est-ce donc ?</p>
<p>Dans <code>/usr/include/alsa/control.h</code> je trouve :</p>
<pre>
<code class="c"><span class="cp">#define snd_ctl_card_info_alloca(ptr) __snd_alloca(ptr, snd_ctl_card_info)</span>
</code>
</pre>
<p>Dans <code>/usr/include/alsa/global.h</code> je trouve :</p>
<pre>
<code class="c"><span class="cp">#define __snd_alloca(ptr,type) do { *ptr = (type##_t *) alloca(type##_sizeof()); memset(*ptr, 0, type##_sizeof()); } while (0)</span>
</code>
</pre>
<p>Aïe, ce sont des macros ! Je ne pourrais donc pas appeler ce code depuis Python !<br />
Mais rusons, ce qu'il nous faut savoir, c'est la taille de l'espace mémoire à allouer…</p>
<p>Nous pourrions chercher la définition exacte de la structure, et la déclarer en Python en héritant de l'objet <a href="http://docs.python.org/3.3/library/ctypes.html#structures-and-unions">Structure</a>, mais cela serait un trop grand effort sans intérêt. En effet nous ne parcourrons jamais manuellement la structure, nous utiliserons des <em>getters</em> pour cela ! Tout ce qui nous importe, c'est donc la taille de la structure à allouer !</p>
<p>Nous n'allons pas jouer au préprocesseur et interpréter nous même ce code. Nous allons lire le code généré par le préprocesseur lorsqu'il rencontre cette macro d'allocation.</p>
<p>Juste avant la ligne qui nous intéresse, j'ajoute un témoin dans le code source d'<code>alsacap</code> (que je renomme au passage en <code>alsacap-trace.c</code>) :</p>
<pre>
<code class="c"><span class="n">snd_ctl_card_info_alloca</span><span class="p">(</span><span class="o">&</span><span class="n">info</span><span class="p">);</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"THDTHDTHD"</span><span class="p">);</span>
</code>
</pre>
<p>Hop, préprocessons tout cela, et regardons :</p>
<pre>
<code class="">gcc -E alsacap-trace.c -lasound | grep -B1 "THDTHDTHD"
do { *&info = (snd_ctl_card_info_t *) __builtin_alloca (snd_ctl_card_info_sizeof()); memset(*&info, 0, snd_ctl_card_info_sizeof()); } while (0);
printf("THDTHDTHD");
</code>
</pre>
<p>Nous trouvons la fonction (c'est est bien une cette fois-ci) <code>snd_ctl_card_info_sizeof()</code> qui retourne la taille mémoire qui doit être allouée ! Essayons…</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">12</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_sizeof</span><span class="p">()</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">12</span><span class="p">]:</span> <span class="mi">376</span>
</code>
</pre>
<p>Mais comment allouons nous avec Python et ctypes ?<br />
Pour allouer un tableau (au sens de C) d'entiers, la <a href="http://docs.python.org/3.3/library/ctypes.html#arrays">documentation nous indique</a> que nous pouvons faire ainsi :</p>
<pre>
<code class="python"><span class="n">TenIntegers</span> <span class="o">=</span> <span class="n">c_int</span> <span class="o">*</span> <span class="mi">10</span>
</code>
</pre>
<p>Pour ne pas nous embêter avec le type (nous ne le connaissons pas vraiment, et peut-il être retranscrit ?), nous allons <em>« multiplier »</em> le type <code>c_void_p</code>. Ainsi nous allouerons un espace mémoire dont l'adresse de départ sera de type « pointeur ».</p>
<p>Juste pour tester, tentons de passer un pointeur vers un espace mémoire de taille arbitraire mais conséquente :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">13</span><span class="p">]:</span> <span class="n">c_info</span> <span class="o">=</span> <span class="p">(</span><span class="n">c_void_p</span> <span class="o">*</span> <span class="mi">50</span><span class="p">)()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">14</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info</span><span class="p">(</span><span class="n">handle</span><span class="p">,</span> <span class="n">c_info</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">14</span><span class="p">]:</span> <span class="mi">0</span>
</code>
</pre>
<p>Notre code ne plante pas, et la fonction retourne le code <em>« no problemo »</em>. Mais attention au <em>« je reviendrai »</em>, dès qu'on va tenter d'utiliser un <em>getter</em> sur <code>c_info</code>, on va prendre le risque d'un <code>segfault</code>…</p>
<p>Nous avons une fonction qui retourne une valeur, mais nous ne pouvons pas <em>« multiplier »</em> cette valeur avec le type, puisque ce type a déjà une taille !</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">15</span><span class="p">]:</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">c_void_p</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">15</span><span class="p">]:</span> <span class="mi">8</span>
</code>
</pre>
<p>Il nous faut donc <em>« multiplier »</em> le type avec la valeur retournée par ladite fonction, divisée par la taille du type. </p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">16</span><span class="p">]:</span> <span class="nb">int</span><span class="p">(</span><span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_sizeof</span><span class="p">()</span> <span class="o">/</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">c_void_p</span><span class="p">))</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">16</span><span class="p">]:</span> <span class="mi">47</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">17</span><span class="p">]:</span> <span class="n">c_info</span> <span class="o">=</span> <span class="p">(</span><span class="n">c_void_p</span> <span class="o">*</span> <span class="nb">int</span><span class="p">(</span><span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_sizeof</span><span class="p">()</span> <span class="o">/</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">c_void_p</span><span class="p">)))()</span>
</code>
</pre>
<p>Nous utilisons <code>int()</code> non parce que la division aurait un reste, mais parce qu'une division retourne un nombre de type <code>float</code>, type qui n'a pas de sens pour une taille mémoire.</p>
<p>Nous avons donc un espace alloué, et il est de bonne taille !<br />
Nous pouvons le vérifier :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">18</span><span class="p">]:</span> <span class="n">sizeof</span><span class="p">(</span><span class="n">c_info</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">18</span><span class="p">]:</span> <span class="mi">376</span>
</code>
</pre>
<p>Nous allons donc l'utiliser !</p>
<h2 id="toc_5">Récupérer les valeurs</h2>
<p>Remplir la structure <code>c_info</code> de ses valeurs, pour la carte sélectionnée :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">19</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info</span><span class="p">(</span><span class="n">c_handle</span><span class="p">,</span> <span class="n">c_info</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">19</span><span class="p">]:</span> <span class="mi">0</span>
</code>
</pre>
<p>Appeler un <em>getter</em> :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">20</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_get_id</span><span class="p">(</span><span class="n">c_info</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">20</span><span class="p">]:</span> <span class="mi">52202712</span>
</code>
</pre>
<p>Nous avons une réponse ! Mais c'est une drôle de réponse !</p>
<p>Mais oui c'est une chaîne de caractère dont nous avons obtenu l'adresse ! Il nous faut définir le type de retour de la fonction et ce sera bon…</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">21</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_get_id</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">22</span><span class="p">]:</span> <span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_get_id</span><span class="p">(</span><span class="n">c_info</span><span class="p">)</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">22</span><span class="p">]:</span> <span class="n">b</span><span class="s">'Intel'</span>
</code>
</pre>
<p>Et voilà ! Désormais il n'y a plus aucun mystère, aucune surprise, ni dans Python, ni dans ctypes, ni dans libasound… Nous pouvons retranscrire tout <code>alsacap</code> avec ce que nous avons appris !</p>
<h2 id="toc_6">Headers et malloc en Python ?</h2>
<p>C'est le petit souci de <code>ctypes</code>, il nous faut reproduire (en partie) une spécificité du langage compilé qu'est le C, les <em>headers</em>. Aussi, nous sommes liés aux contraintes d'un langage aujourd'hui considéré comme bas-niveau qu'est le C, comme les allocations mémoires…</p>
<p>Par exemple j'ai du écrire ces déclarations :</p>
<pre>
<code class="python"><span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_get_id</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">libasound</span><span class="o">.</span><span class="n">snd_ctl_card_info_get_name</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">libasound</span><span class="o">.</span><span class="n">snd_pcm_info_get_id</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">libasound</span><span class="o">.</span><span class="n">snd_pcm_info_get_name</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">libasound</span><span class="o">.</span><span class="n">snd_pcm_info_get_subdevice_name</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">libasound</span><span class="o">.</span><span class="n">snd_pcm_format_name</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_char_p</span>
<span class="n">libasound</span><span class="o">.</span><span class="n">snd_pcm_format_mask_test</span><span class="o">.</span><span class="n">restype</span> <span class="o">=</span> <span class="n">c_bool</span>
</code>
</pre>
<p>Ces allocations :</p>
<pre>
<code class="">c_info = (c_void_p * int(self.libasound.snd_ctl_card_info_sizeof() / sizeof(c_void_p)))()
c_pcminfo = (c_void_p * int(self.libasound.snd_pcm_info_sizeof() / sizeof(c_void_p)))()
c_pars = (c_void_p * int(self.libasound.snd_pcm_hw_params_sizeof() / sizeof(c_void_p)))()
c_fmask = (c_void_p * int(self.libasound.snd_pcm_format_mask_sizeof() / sizeof(c_void_p)))()
</code>
</pre>
<p>Et puis j'ai reproduit un certain nombre de constantes de <code>libasound</code> :</p>
<p>En C nous avons :</p>
<pre>
<code class="c"><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">include</span><span class="o">/</span><span class="n">alsa</span><span class="o">/</span><span class="n">pcm</span><span class="p">.</span><span class="n">h</span>
<span class="o">**</span> <span class="n">PCM</span> <span class="n">sample</span> <span class="n">format</span> <span class="err">*/</span>
<span class="k">typedef</span> <span class="k">enum</span> <span class="n">_snd_pcm_format</span> <span class="p">{</span>
<span class="cm">/** Unknown */</span>
<span class="n">SND_PCM_FORMAT_UNKNOWN</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span>
<span class="cm">/** Signed 8 bit */</span>
<span class="n">SND_PCM_FORMAT_S8</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="cm">/** Unsigned 8 bit */</span>
<span class="n">SND_PCM_FORMAT_U8</span><span class="p">,</span>
<span class="cm">/** Signed 16 bit Little Endian */</span>
<span class="n">SND_PCM_FORMAT_S16_LE</span><span class="p">,</span>
<span class="cm">/** Signed 16 bit Big Endian */</span>
<span class="n">SND_PCM_FORMAT_S16_BE</span><span class="p">,</span>
<span class="p">[</span><span class="err">…</span><span class="p">]</span>
<span class="n">SND_PCM_FORMAT_U18_3BE</span><span class="p">,</span>
<span class="n">SND_PCM_FORMAT_LAST</span> <span class="o">=</span> <span class="n">SND_PCM_FORMAT_U18_3BE</span><span class="p">,</span>
</code>
</pre>
<p>J'ai donc écrit en Python :</p>
<pre>
<code class="python"><span class="n">SND_PCM_FORMAT_S8</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">SND_PCM_FORMAT_U8</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="n">SND_PCM_FORMAT_S16_LE</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">SND_PCM_FORMAT_S16_BE</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="c"># […]</span>
<span class="n">SND_PCM_FORMAT_U18_3BE</span> <span class="o">=</span> <span class="n">c_int</span><span class="p">(</span><span class="mi">43</span><span class="p">)</span>
<span class="n">SND_PCM_FORMAT_LAST</span> <span class="o">=</span> <span class="n">SND_PCM_FORMAT_U18_3BE</span>
</code>
</pre>
<p>Mais rassurez-vous, je n'ai pas écrit tout cela à la main, <code>sed</code> m'a rendu un bien grand service !</p>
<h2 id="toc_7">PyAlsaCap</h2>
<p>Et voilà, je n'ai plus eu qu'à suivre méthodiquement le code source d'<code>alsacap</code> et à dérouler avec lui la grande boucle qui interroge les <em>cards</em>, puis les <em>devices</em>, puis les <em>subdevices</em> etc.</p>
<p>Cependant, contrairement à <code>alsacap</code>, je n'imprime pas sur la sortie standard ce que je trouve, je remplis une grande structure utilisable depuis Python ! Pour cela j'ai donc écrit la fonction <code>GetCards</code>. Regardons ce qu'elle nous retourn :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">pyalsacap</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">insp</span> <span class="o">=</span> <span class="n">Inspector</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="n">cards</span> <span class="o">=</span> <span class="n">insp</span><span class="o">.</span><span class="n">GetCards</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="n">cards</span>
<span class="n">Out</span><span class="p">[</span><span class="mi">4</span><span class="p">]:</span>
<span class="p">{</span>
<span class="mi">0</span><span class="p">:</span> <span class="p">{</span>
<span class="s">'id'</span><span class="p">:</span> <span class="s">'Intel'</span><span class="p">,</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'HDA Intel'</span><span class="p">,</span>
<span class="s">'devices'</span><span class="p">:</span> <span class="p">{</span>
<span class="mi">0</span><span class="p">:</span> <span class="p">{</span>
<span class="s">'channels'</span><span class="p">:</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span>
<span class="s">'formats'</span><span class="p">:</span> <span class="p">[</span><span class="s">'S16_LE'</span><span class="p">],</span>
<span class="s">'id'</span><span class="p">:</span> <span class="s">'AD198x Analog'</span><span class="p">,</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'AD198x Analog'</span><span class="p">,</span>
<span class="s">'rate'</span><span class="p">:</span> <span class="p">[</span><span class="mi">8000</span><span class="p">,</span> <span class="mi">192000</span><span class="p">],</span>
<span class="s">'subdevices_available'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="s">'subdevices'</span><span class="p">:</span> <span class="p">{</span>
<span class="mi">0</span><span class="p">:</span> <span class="p">{</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'subdevice #0'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="mi">1</span><span class="p">:</span> <span class="p">{</span>
<span class="s">'channels'</span><span class="p">:</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span>
<span class="s">'formats'</span><span class="p">:</span> <span class="p">[</span><span class="s">'S16_LE'</span><span class="p">],</span>
<span class="s">'id'</span><span class="p">:</span> <span class="s">'AD198x Digital'</span><span class="p">,</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'AD198x Digital'</span><span class="p">,</span>
<span class="s">'rate'</span><span class="p">:</span> <span class="p">[</span><span class="mi">44100</span><span class="p">,</span> <span class="mi">192000</span><span class="p">],</span>
<span class="s">'subdevices_available'</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span>
<span class="s">'subdevices'</span><span class="p">:</span> <span class="p">{</span>
<span class="mi">0</span><span class="p">:</span> <span class="p">{</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'subdevice #0'</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">},</span>
<span class="mi">29</span><span class="p">:</span> <span class="p">{</span>
<span class="s">'id'</span><span class="p">:</span> <span class="s">'ThinkPadEC'</span><span class="p">,</span>
<span class="s">'name'</span><span class="p">:</span> <span class="s">'ThinkPad Console Audio Control'</span><span class="p">,</span>
<span class="s">'devices'</span><span class="p">:</span> <span class="p">{</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code>
</pre>
<p>C'est beau !</p>
<p>J'ai ajouté une autre fonction nommée <code>PrintCards</code> qui reparcourt la structure et l'imprime sur la sortie standard. Regardons ce qu'elle nous écrit !</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">5</span><span class="p">]:</span> <span class="n">desc</span> <span class="o">=</span> <span class="n">Descriptor</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="n">desc</span><span class="o">.</span><span class="n">PrintCards</span><span class="p">(</span><span class="n">cards</span><span class="p">)</span>
<span class="o">***</span> <span class="n">Scanning</span> <span class="k">for</span> <span class="n">playback</span> <span class="n">devices</span> <span class="o">***</span>
<span class="n">Card</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`Intel', name `</span><span class="n">HDA</span> <span class="n">Intel</span><span class="s">'</span>
<span class="n">Device</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`AD198x Analog', name `</span><span class="n">AD198x</span> <span class="n">Analog</span><span class="s">', 1 subdevices (1 available</span>
<span class="mi">2</span> <span class="n">channels</span><span class="p">,</span> <span class="n">sampling</span> <span class="n">rate</span> <span class="mf">8000.</span><span class="o">.</span><span class="mi">192000</span> <span class="n">Hz</span>
<span class="n">Sample</span> <span class="n">formats</span><span class="p">:</span> <span class="n">S16_LE</span>
<span class="n">Subdevice</span> <span class="mi">0</span><span class="p">,</span> <span class="n">name</span> <span class="err">`</span><span class="n">subdevice</span> <span class="c">#0'</span>
<span class="n">Device</span> <span class="mi">1</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`AD198x Digital', name `</span><span class="n">AD198x</span> <span class="n">Digital</span><span class="s">', 1 subdevices (1 available)</span>
<span class="mi">2</span> <span class="n">channels</span><span class="p">,</span> <span class="n">sampling</span> <span class="n">rate</span> <span class="mf">44100.</span><span class="o">.</span><span class="mi">192000</span> <span class="n">Hz</span>
<span class="n">Sample</span> <span class="n">formats</span><span class="p">:</span> <span class="n">S16_LE</span>
<span class="n">Subdevice</span> <span class="mi">0</span><span class="p">,</span> <span class="n">name</span> <span class="err">`</span><span class="n">subdevice</span> <span class="c">#0'</span>
<span class="n">Card</span> <span class="mi">29</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`ThinkPadEC', name `</span><span class="n">ThinkPad</span> <span class="n">Console</span> <span class="n">Audio</span> <span class="n">Control</span><span class="s">'</span>
</code>
</pre>
<p>Cela nous convient très bien !</p>
<p>Certains demanderont <em>« mais pourquoi deux classes ? »</em>. En fait j'ai fait deux classes parce que je considère que la partie qui imprime n'est utile que lorsque PyAlsaCap est utilisé comme un script (en remplacement d'<code>alsacap</code>). Lorsque nous utilisons PyAlsaCap comme un module (ce pourquoi je l'ai écrit), nous n'avons pas besoin d'importer tout ce qui a trait à l'impression sur écran !</p>
<p>Pour le moment, toutes les fonctionnalité d'<code>alsacap</code> ne sont pas reproduites, nous ne pouvons pas interroger une carte précise, nous pouvons seulement lister toutes les cartes selon une fonction précise (lecture ou enregistrement). Le paramètre <code>-R</code> est donc pour le moment le seul paramètre que prend le script. Pour la fonction <code>GetCards</code>, l'argument équivalent est <code>"capture"</code>.</p>
<p>En Python :</p>
<pre>
<code class="python"><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">pyalsacap</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">insp</span> <span class="o">=</span> <span class="n">Inspector</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="n">desc</span> <span class="o">=</span> <span class="n">Descriptor</span><span class="p">()</span>
<span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="n">desc</span><span class="o">.</span><span class="n">PrintCards</span><span class="p">(</span><span class="n">insp</span><span class="o">.</span><span class="n">GetCards</span><span class="p">(</span><span class="s">"capture"</span><span class="p">)</span>
<span class="o">***</span> <span class="n">Scanning</span> <span class="k">for</span> <span class="n">recording</span> <span class="n">devices</span> <span class="o">***</span>
<span class="n">Card</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`Intel', name `</span><span class="n">HDA</span> <span class="n">Intel</span><span class="s">'</span>
<span class="n">Device</span> <span class="mi">0</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`AD198x Analog', name `</span><span class="n">AD198x</span> <span class="n">Analog</span><span class="s">', 2 subdevices (2 available</span>
<span class="mi">2</span> <span class="n">channels</span><span class="p">,</span> <span class="n">sampling</span> <span class="n">rate</span> <span class="mf">8000.</span><span class="o">.</span><span class="mi">192000</span> <span class="n">Hz</span>
<span class="n">Sample</span> <span class="n">formats</span><span class="p">:</span> <span class="n">S16_LE</span>
<span class="n">Subdevice</span> <span class="mi">0</span><span class="p">,</span> <span class="n">name</span> <span class="err">`</span><span class="n">subdevice</span> <span class="c">#0'</span>
<span class="n">Subdevice</span> <span class="mi">1</span><span class="p">,</span> <span class="n">name</span> <span class="err">`</span><span class="n">subdevice</span> <span class="c">#0'</span>
<span class="n">Card</span> <span class="mi">29</span><span class="p">,</span> <span class="n">ID</span> <span class="sb">`ThinkPadEC', name `</span><span class="n">ThinkPad</span> <span class="n">Console</span> <span class="n">Audio</span> <span class="n">Control</span><span class="s">'</span>
</code>
</pre>
<p>Depuis le <em>shell</em> :</p>
<pre>
<code class="">$ ./pyalsacap.py -R
*** Scanning for recording devices ***
Card 0, ID `Intel', name `HDA Intel'
Device 0, ID `AD198x Analog', name `AD198x Analog', 2 subdevices (2 available)
2 channels, sampling rate 8000..192000 Hz
Sample formats: S16_LE
Subdevice 0, name `subdevice #0'
Subdevice 1, name `subdevice #0'
Card 29, ID `ThinkPadEC', name `ThinkPad Console Audio Control'
</code>
</pre>
<p>À comparer avec la sortie d'<code>alsacap</code> :</p>
<pre>
<code class="">$ ./alsacap -R
*** Scanning for recording devices ***
Card 0, ID `Intel', name `HDA Intel'
Device 0, ID `AD198x Analog', name `AD198x Analog', 2 subdevices (2 available)
2 channels, sampling rate 8000..192000 Hz
Sample formats: S16_LE, S32_LE
Subdevice 0, name `subdevice #0'
Subdevice 1, name `subdevice #1'
Card 29, ID `ThinkPadEC', name `ThinkPad Console Audio Control'
</code>
</pre><h2 id="toc_8">Le code</h2>
<p>Si cela vous intéresse, vous pouvez lire <a href="http://dl.illwieckz.net/u/thomas-debesse/bazar/pyalsacap/pyalsacap.py.txt">le source de PyAlsaCap</a> à la date du 10 mars.</p>
<p>C'est ce que j'ai écrit pour occuper mon dimanche après-midi. Il est probable que le code évolue et que j'y ajoute quelques fonctions supplémentaires (et peut-être pourra-t-il complètement remplacer <code>alsacap</code>).</p>
<p>Il faudra aussi que j'apprenne à utiliser le module <a href="http://docs.python.org/3.3/library/argparse.html">argparse</a> afin de prendre en compte correctement les paramètres de la ligne de commande.</p>
<p>Mais déjà, si PyAlsaCap ne fait pas tout ce que fait <code>alsacap</code>, il fait quelque chose qu'<code>alsacap</code> ne permet pas : être utilisé comme module Python 3 ! Et c'est là son grand avantage : le code de PyAlsaCap n'utilise rien de spécifique à Python 3 par rapport à Python 2, mais il est utilisable dans un projet plus vaste en Python 3, et ce sans compilation.</p>
<ul><li>PyAlsaCap est compatible Python 3</li>
<li>PyAlsaCap n'a pas d'autres dépendances que Python et <code>libasound2</code>.</li>
<li>Contrairement à d'autres modules (<code>python-pyalsa</code>, <code>python-alsaaudio</code>), PyAlsaCap est un module en python qui n'a pas besoin d'être compilé.</li>
<li>PyAlsaCap est donc utilisable dans toute distribution GNU/Linux qui fournit Python et <code>libasound</code>, dès aujourd'hui.</li>
<li>PyAlsaCap ne fait pas beaucoup de choses, mais c'est peut-être aussi un avantage ! PyAlsaCap liste une sélection de caractéristiques et capacités des cartes audio, et il le fait bien. :)</li>
</ul><h2 id="toc_9">Conclusion</h2>
<p>Python avec ctypes, c'est extrêmement puissant !</p>
<p>Bon, parfois, chercher par quel bout prendre la bibliothèque C pour obtenir ce que l'on souhaite est un peu prise de tête, surtout lorsque le code fait usage de macros !</p>
<p>Parfois la recherche de solution prend des airs de rétro-ingénierie, mais sur du code ouvert, c'est vraiment trop facile. :)</p>
<p>Python avec ctypes, c'est grisant…</p>
<p>Ça m'a rappelé les sensations que j'avais quand je codait en C étant enfant, mais cette fois-ci sans compilation ! Les pointeurs, c'est fun ! Les langages interprétés, c'est fun aussi ! On peut combiner les deux… :o</p>
<p>D'ailleurs je crois que c'est une bonne manière de décrire ctypes : on n'échappe pas au C, il faut y avoir été initié et maîtriser les concepts de bases, mais par contre on évite toute la compilation !</p>
<p>Surtout, surtout, ce qui est super sympa, c'est de pouvoir jouer avec des pointeurs avec un langage interprété, depuis un <em>shell</em> dudit langage… De quoi nous occuper les longues soirées d'hiver, même si l'hiver ne dure pas très longtemps sur la côte d'Azur ! :p</p><div><a href="https://linuxfr.org/users/illwieckz/journaux/pyalsacap-python-pointeurs-et-cartes-sons.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/97684/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/illwieckz/journaux/pyalsacap-python-pointeurs-et-cartes-sons#comments">ouvrir dans le navigateur</a>
</p>
Thomas Debessehttps://linuxfr.org/nodes/97684/comments.atomtag:linuxfr.org,2005:Diary/329862012-08-17T17:23:22+02:002012-08-17T17:23:22+02:00Et Dieu inventa le soutien gorge !Licence CC By‑SA http://creativecommons.org/licenses/by-sa/3.0/deed.fr<p>Le C est connu pour ses pointeurs. Les pointeurs sont une merveille pour certains, une horreur pour d'autre. Je sais qu'il s'agit d'un nième débat religieux par ici, mais parlons de C et de pointeurs !</p>
<p>Un vrai moment de détente pour le week-end :D</p>
<p>Le noyau Linux utilise une forme particulière de listes chaînées qui nous permet d'apprécier ce genre de code <a href="http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=include/linux/kernel.h;h=604382143bcfccd6772260ed56e16589800af83c;hb=HEAD">include/linux/kernel.h:683</a>:</p>
<pre>
<code class="c"><span class="cp">#define container_of(ptr, type, member) ({ \</span>
<span class="cp"> const typeof( ((type *)0)->member ) *__mptr = (ptr); \</span>
<span class="cp"> (type *)( (char *)__mptr - offsetof(type,member) );})</span>
</code>
</pre>
<p>Et quand mon apprenti tombe sur ce genre de truc, je me dois de lui expliquer.<br />
Pour ceux qui se posent la question, ce petit code permet de trouver l'adresse d'une structure à l'aide de l'adresse d'un membre de la structure.</p>
<p>Imaginons la structure suivante :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">x</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">a</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">b</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">x</span> <span class="n">X</span><span class="p">;</span>
</code>
</pre>
<p>Et admettons :</p>
<ul><li>X est à l'adresse <code>0x14C4</code></li>
<li>X.a est à l'adresse <code>0x14C8</code></li>
<li>X.b est à l'adresse <code>0x14CC</code></li>
</ul><p>Donc, si ma structure X était à l'adresse <code>0x0000</code>, nous aurions :</p>
<ul><li>X.a à l'adresse <code>0x0004</code></li>
<li>X.b à l'adresse <code>0x0008</code></li>
</ul><p>Donc si j'ai l'adresse de X.a, j'ai l'adresse de X, car :</p>
<pre>
<code class="c"><span class="o">&</span><span class="n">X</span> <span class="o">==</span> <span class="o">&</span><span class="p">(</span><span class="n">X</span><span class="p">.</span><span class="n">a</span><span class="p">)</span> <span class="o">-</span> <span class="p">((</span><span class="k">struct</span> <span class="n">x</span> <span class="o">*</span><span class="p">)</span><span class="mi">0</span><span class="p">)</span><span class="o">-></span><span class="n">a</span><span class="p">;</span>
</code>
</pre>
<p>Tout ceci est <a href="http://c-faq.com/struct/offsetof.html">bien connu</a> et est disponible dans <code>stddef.h</code> à l'aide de la macro <code>offsetof</code> :</p>
<pre>
<code class="c"><span class="cp">#include <stddef.h></span>
<span class="n">offsetof</span><span class="p">(</span><span class="k">struct</span> <span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
</code>
</pre>
<p>Tous ces éléments mis ensemble nous permettent d'obtenir des structures de données un peu différentes :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">s_list</span> <span class="p">{</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">previous</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">s_structure_x</span> <span class="p">{</span>
<span class="cm">/* membres */</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="n">list</span><span class="p">;</span>
<span class="p">};</span>
</code>
</pre>
<p>En lieu et place de :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">s_structure_x</span> <span class="p">{</span>
<span class="cm">/* membres */</span>
<span class="k">struct</span> <span class="n">s_structure_x</span> <span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_structure_x</span> <span class="o">*</span> <span class="n">previous</span><span class="p">;</span>
<span class="p">};</span>
</code>
</pre>
<p>ou encore :</p>
<pre>
<code class="c"><span class="k">struct</span> <span class="n">s_list</span> <span class="p">{</span>
<span class="kt">void</span> <span class="o">*</span> <span class="n">data</span><span class="p">;</span>
<span class="kt">size_t</span> <span class="n">length</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">type</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">next</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_list</span> <span class="o">*</span> <span class="n">previous</span><span class="p">;</span>
<span class="p">};</span>
</code>
</pre>
<p>et variantes.</p>
<p>Voilà, je suis pas certain d'être clair, mais je fais vite car ce n'est pas le sujet. En expliquant ce sujet, une idée m'est venue (et ça c'est tout l'intérêt de former des jeunes) pour mon projet actuel. Dans les grandes lignes, le projet est de faire communiquer des appareils entre eux.<br />
Afin de faire ça proprement, j'ai, bien entendu, défini quelques couches, 3 en l'occurrence et chacune encapsule l'autre. Un couche réseau, une couche application et une couche donnée.</p>
<p>Dans la réflexion, le fait de travailler sur des micro-contrôleurs n'ayant que 512 octets de mémoires RAM et que la quantité de données transmises peut atteindre 150 octets, plus quelques 10 octets pour les couches, un encodage des données qui ajoutent une 20aine d'octets (1 bit perdu par octet transmis) et quelques variables d'état, l'utilisation de mémoire tampon pour le réseau est à proscrire ; on arrive presque à la moitié de la RAM juste pour les communications (dont la majeure partie n'est que ce qui se trouve déjà en mémoire mais sous une autre forme).</p>
<p>Le code réseau est donc prévu pour travailler en flux. Les données sont transformées et transmises à mesure. Quelques variables d'états plus tard, le réseau ne coûte qu'une ou deux dizaines d'octets en mémoire.</p>
<p>Le problème vient de l'enchaînement des couches dans le code, par exemple :</p>
<pre>
<code class="c"><span class="kt">void</span> <span class="nf">hw_send</span><span class="p">(</span> <span class="cm">/* ... */</span> <span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="n">l1_send</span><span class="p">();</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">l1_send</span><span class="p">(</span> <span class="cm">/* ... */</span> <span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="n">l2_send</span><span class="p">();</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="cm">/* ... */</span>
</code>
</pre>
<p>Ça marche, mais si je veux, par exemple, ajouter un traitement (chiffrement ?) entre la couche 1 et 2, le code doit être modifié de manière dramatique. Ou si je veux réutiliser la couche 2 dans une couche 1bis. Non ce qu'il me faut, c'est une sorte de liste chaînée qui traversent les couches et qui me permettent, le cas échéant, d'intercaler un traitement particulier.</p>
<p>Et du noyau vint la solution qui me semble, pour l'instant, la plus intéressante (on verra si ça tient jusqu'à lundi (dans mon esprit (dégénéré))). J'ai fais un petit test et ça donne ça :</p>
<pre>
<code class="c"><span class="cp">#include <stdio.h></span>
<span class="cp">#include <string.h></span>
<span class="cp">#include <stddef.h></span>
<span class="cm">/* Structure pour mes fonctions de communications </span>
<span class="cm"> *</span>
<span class="cm"> * send et receive reçoivent en premier paramètre une variable </span>
<span class="cm"> * (ComFunctions *). Le deuxième paramètre de send est une variable d'état,</span>
<span class="cm"> * quand elle est à 0xFF, il n'y a plus rien à envoyer. Pour receive, il </span>
<span class="cm"> * s'agit de l'octet reçu par le réseau.</span>
<span class="cm"> * send retourne l'octet à transmettre et receive l'état qui, une fois à</span>
<span class="cm"> * 0xFF indique qu'il n'y a plus rien à recevoir.</span>
<span class="cm"> * La variable lower contient l'adresse de la couche en-dessous de l'actuelle.</span>
<span class="cm"> */</span>
<span class="k">typedef</span> <span class="k">struct</span> <span class="n">s_com_funcs</span> <span class="n">ComFunctions</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">s_com_funcs</span> <span class="p">{</span>
<span class="n">ComFunctions</span> <span class="o">*</span> <span class="n">lower</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="p">(</span><span class="o">*</span><span class="n">send</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span><span class="p">);</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="p">(</span><span class="o">*</span><span class="n">receive</span><span class="p">)(</span><span class="kt">void</span> <span class="o">*</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span><span class="p">);</span>
<span class="p">};</span>
<span class="cm">/* Mes structures avec les valeurs nécessaires pour chaque couche ainsi</span>
<span class="cm"> * qu'une instance de ComFunctions.</span>
<span class="cm"> */</span>
<span class="k">struct</span> <span class="n">l1</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val1</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val2</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val3</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">state</span><span class="p">;</span>
<span class="n">ComFunctions</span> <span class="n">f</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">l2</span> <span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val1</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">val2</span><span class="p">;</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">state</span><span class="p">;</span>
<span class="n">ComFunctions</span> <span class="n">f</span><span class="p">;</span>
<span class="p">};</span>
<span class="cm">/* La fonction send. En paramètre, nous avons la couche la plus haute,</span>
<span class="cm"> * retourne 0 quand la transmission est terminée (pour permettre de faire</span>
<span class="cm"> * while(send(...));)</span>
<span class="cm"> */</span>
<span class="kt">char</span> <span class="nf">send</span><span class="p">(</span><span class="n">ComFunctions</span> <span class="o">*</span> <span class="n">highest</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">res</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"0x%02X "</span><span class="p">,</span> <span class="n">highest</span><span class="o">-></span><span class="n">send</span><span class="p">(</span><span class="n">highest</span><span class="p">,</span> <span class="o">&</span><span class="n">res</span><span class="p">));</span>
<span class="k">if</span><span class="p">(</span><span class="n">res</span><span class="o">==</span><span class="mh">0xFF</span><span class="p">)</span> <span class="p">{</span>
<span class="n">printf</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="cm">/* Des init bidons, juste pour le test */</span>
<span class="kt">void</span> <span class="nf">init_l2</span><span class="p">(</span><span class="k">struct</span> <span class="n">l2</span> <span class="o">*</span> <span class="n">me</span><span class="p">)</span> <span class="p">{</span> <span class="n">me</span><span class="o">-></span><span class="n">val1</span><span class="o">=</span><span class="sc">'Z'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">val2</span><span class="o">=</span><span class="sc">'F'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span> <span class="p">}</span>
<span class="kt">void</span> <span class="nf">init_l1</span><span class="p">(</span><span class="k">struct</span> <span class="n">l1</span> <span class="o">*</span> <span class="n">me</span><span class="p">)</span> <span class="p">{</span> <span class="n">me</span><span class="o">-></span><span class="n">val1</span><span class="o">=</span><span class="sc">'a'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">val2</span><span class="o">=</span><span class="sc">'b'</span><span class="p">;</span> <span class="n">me</span><span class="o">-></span><span class="n">val3</span><span class="o">=</span><span class="sc">'c'</span><span class="p">;</span>
<span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span> <span class="p">}</span>
<span class="cm">/* Des fonctions send, juste un aperçu, mais le code est trivial */</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="nf">send_l2</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span> <span class="n">com_funcs</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">state</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">l2</span> <span class="o">*</span> <span class="n">me</span><span class="o">=</span><span class="p">(</span><span class="k">struct</span> <span class="n">l2</span> <span class="o">*</span><span class="p">)((</span><span class="n">com_funcs</span><span class="p">)</span><span class="o">-</span><span class="n">offsetof</span><span class="p">(</span><span class="k">struct</span> <span class="n">l2</span><span class="p">,</span> <span class="n">f</span><span class="p">));</span>
<span class="cm">/* ... */</span>
<span class="p">}</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="nf">send_l1</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span> <span class="n">com_funcs</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">char</span> <span class="o">*</span> <span class="n">state</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">struct</span> <span class="n">l1</span> <span class="o">*</span> <span class="n">me</span><span class="o">=</span><span class="p">(</span><span class="k">struct</span> <span class="n">l1</span> <span class="o">*</span><span class="p">)((</span><span class="n">com_funcs</span><span class="p">)</span><span class="o">-</span><span class="n">offsetof</span><span class="p">(</span><span class="k">struct</span> <span class="n">l1</span><span class="p">,</span> <span class="n">f</span><span class="p">));</span>
<span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">lower_state</span><span class="o">=</span><span class="mh">0x00</span><span class="p">,</span> <span class="n">tmp</span><span class="o">=</span><span class="mh">0x00</span><span class="p">;</span>
<span class="k">switch</span><span class="p">(</span><span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="p">)</span> <span class="p">{</span>
<span class="cm">/* ... plein de code ... */</span>
<span class="k">case</span> <span class="mi">2</span>:
<span class="n">tmp</span><span class="o">=</span><span class="sc">'!'</span><span class="p">;</span>
<span class="k">if</span><span class="p">(</span><span class="n">me</span><span class="o">-></span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">==</span><span class="nb">NULL</span><span class="p">)</span> <span class="p">{</span>
<span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="n">tmp</span><span class="o">=</span><span class="n">me</span><span class="o">-></span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">-></span><span class="n">send</span><span class="p">(</span><span class="n">me</span><span class="o">-></span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="p">,</span> <span class="o">&</span><span class="n">lower_state</span><span class="p">);</span>
<span class="k">if</span><span class="p">(</span><span class="n">lower_state</span><span class="o">==</span><span class="mh">0xFF</span><span class="p">)</span> <span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">*</span><span class="n">state</span><span class="o">=</span><span class="n">me</span><span class="o">-></span><span class="n">state</span><span class="p">;</span>
<span class="k">return</span> <span class="n">tmp</span><span class="p">;</span>
<span class="cm">/* ... encore plein de code ... */</span>
<span class="p">}</span>
<span class="cm">/* Une fonction main */</span>
<span class="kt">int</span> <span class="n">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span> <span class="n">argv</span><span class="p">)</span>
<span class="p">{</span>
<span class="cm">/* Que deux couches, trop flemmard pour la troisième ^^ */</span>
<span class="k">struct</span> <span class="n">l1</span> <span class="n">x</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">l2</span> <span class="n">y</span><span class="p">;</span>
<span class="n">init_l1</span><span class="p">(</span><span class="o">&</span><span class="n">x</span><span class="p">);</span>
<span class="n">init_l2</span><span class="p">(</span><span class="o">&</span><span class="n">y</span><span class="p">);</span>
<span class="n">x</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">=&</span><span class="p">(</span><span class="n">y</span><span class="p">.</span><span class="n">f</span><span class="p">);</span>
<span class="n">x</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">send</span><span class="o">=</span><span class="n">send_l1</span><span class="p">;</span>
<span class="n">y</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">lower</span><span class="o">=</span><span class="nb">NULL</span><span class="p">;</span>
<span class="n">y</span><span class="p">.</span><span class="n">f</span><span class="p">.</span><span class="n">send</span><span class="o">=</span><span class="n">send_l2</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">send</span><span class="p">(</span><span class="o">&</span><span class="p">(</span><span class="n">x</span><span class="p">.</span><span class="n">f</span><span class="p">)));</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code>
</pre>
<p>Donc le fonctionnement est très simple, chaque couche peut appeler la couche sous-jacente et traiter son résultat avant de l'intégrer dans sa propre sortie. Ça permet d'intégrer des filtres entre les couches (j'ai testé avec un chiffrement fort (xor :-p), c'est démentiel).<br />
Rien de bien révolutionnaire (déjà je travail pas chez Apple, c'est donc mal parti) et certainement déjà utilisé dans bien des projets, mais je cherchais juste une excuse pour faire un journal sur les pointeurs C (et les pointeurs de fonctions). Et ça fait jamais de mal de revoir un peu de C :)</p>
<p>Ah ! et je voulais aussi que ça ce sache : j'aime quand ça pointe !</p><div><a href="https://linuxfr.org/users/tchetch/journaux/et-dieu-inventa-le-soutien-gorge.epub">Télécharger ce contenu au format EPUB</a></div> <p>
<strong>Commentaires :</strong>
<a href="//linuxfr.org/nodes/95222/comments.atom">voir le flux Atom</a>
<a href="https://linuxfr.org/users/tchetch/journaux/et-dieu-inventa-le-soutien-gorge#comments">ouvrir dans le navigateur</a>
</p>
Etienne Bagnoudhttps://linuxfr.org/nodes/95222/comments.atom