Nouveau

Mes plugins Eclipse

Cela fait des années que j’utilise Eclipse, par choix ou par obligation. On dira ce qu’on voudra, c’est quand même un IDE très puissant et la multitude de plugins disponibles contribue à sa versatilité quand tu travailles avec plusieurs langages pour un même projet. Cet article présente quelques plugins que j’utilise et s’enrichira au fil des découvertes 🙂

Eclipse logo

CMake Editor

CMake Editor rajoute un éditeur dédiés aux fichiers CMake. Il propose bien sûr la coloration syntaxique et l’autocomplétion :

eclipse-plugin-cmake-editor-autocompletion

Une entrée est créée dans Windows / Preferences / CMakeEd pour configurer la coloration syntaxique et pour gérer ses templates, qui sont accessibles via l’autocomplétion.

Enfin, l’éditeur est par défaut associé aux fichiers CMake :

eclipse-plugin-cmake-editor-association

CMake Editor est disponible sur le marketplace.

Eclox

Eclox permet une intégration rapide de Doxygen dans Eclipse. Vous pouvez le configurer dans Windows / Preferences / Doxygen :

Il est aussi très utile de choisir Doxygen comme outil de documentation dans les options de l’éditeur C/C++. Ainsi, quand taperez /** (ou /*! ou peut-être d’autres débuts de commentaire Doxygen valide) juste avant le corps d’une fonction, d’une énumération, d’une variable, un bloc de documentation Doxygen sera automatiquement créé.

Le plugin offre aussi un éditeur pour les fichiers Doxygen :

Enfin, un bouton avec un @ bleu apparaît dans la barre d’outils pour générer la documentation en un clic !

Eclox est disponible sur le martketplace.

JSON Editor Plugin

On a toujours besoin d’un éditeur JSON. JSON Editor Pluging fait bien le taf. Une entrée dans les préférences permet de configurer le formatage et la coloration syntaxique. Le plus est que la structure du fichier JSON est montrée dans la vue Outline :

JSON Editor Plugin est disponible sur le marketplace.

On a toujours besoin d’un éditeur de **Markdown**.

Markdown Text Editor

Markdown, what else? Idéal pour faire des README et autres petites documentations.

le plugin apporte un éditeur avec deux onglets; Le premier est l’éditeur en mode texte, avec un rendu un peu WYSIWYG :

eclipse markdown 1 texte

Le second est une prévisualisation du rendu :

eclipse markdown 2 preview

Malheureusement, il ne semble pas possible d’afficher les deux onglets en même temps, pour voir le rendu quand on tape du texte…

Des soft wraps sont fait l’éditeur pour s’adapter à la largeur de la vue et ça c’est cool.

Enfin, la vue Outline affiche la table des matières du fichier. On peut cliquer sur un titre pour le sélectionner dans l’éditeur.

eclipse markdown 3 outline

_Markdown Text Editor_ est disponible sur le [marketplace](http://marketplace.eclipse.org/content/markdown-text-editor).

QuickImage

Quand vous travaillez sur des GUI, vous avez souvent des images à gérer. Le plugin Quick Image est une simple visionneuse d’images, indiquant quelques informations basiques, suffisante pour ce que j’avais à faire :

eclipse-plugin-quick-image

QuickImage est disponible sur le marketplace.

Publicités

Lister les répertoires présents dans le path

J’étais (malheureusement) déjà grand quand j’ai découvert awk :

Bien qu’il soit rebutant aux premiers abords, on arrive assez rapidement à faire des choses sympathiques !

Cet article vous montre une ligne simple pour lister les répertoires présents dans le path grâce à awk. La première chose à faire est d’envoyer le contenu de la variable d’environnement PATH à awk grâce à un pipe. Le programme awk commence  par changer le séparateur de champ (FS pour Field Separator) dans BEGIN puis boucle pour afficher les champs.

Voici la commande complète pour Unix :

echo $PATH | awk 'BEGIN {FS=":"} { for(i = 1; i <= NF; i++) print $i}'

Sous Windows, le séparateur est différent et la récupération de la valeur de la variable d’environnement est différente. De plus, Cmder (la ligne de commande que j’utilise depuis près d’un an et demi sous Windows) n’aime pas les chevrons donc j’utilise une boucle while à la place de la boucle for :

echo %PATH% | awk 'BEGIN {FS=";"} { i = 0; while(i++ != NF) print $i}'

Pour finir, notez que la boucle commence à l’indice 1 et termine à l’indice NF (pour Number of Fields) inclus puisque $0 est l’argument entier et que $1 à $n correspondent aux morceaux de l’argument découpé selon FS.

std::optional du C++17

C++17 a apporté son lot de nouvelles fonctionnalités et l’une est l’ajout de std::optional<class T>. Comme son nom l’indique très bien, c’est une classe template qui contient une valeur optionnelle. Pratique quand vous voulez écrire une fonction qui renvoie une valeur et en même temps signaler qu’il est possible que cette valeur soit invalide et ne doive pas être utilisée. Pour plus de détails sur cette fonctionnalité, vous pouvez bien sûr lire le très bon article de cppreference.

Pour l’exemple, j’ai fait écrit une fonction qui tente de charger une image. Comme le chargement peut échouer, elle ne renvoie pas directement une instance de la classe Image mais un std::optional<Image> pour wrapper une image qui peut ne pas être présente. Voici le code :

Rien de bien sorcier. Une classe Image avec ici juste le chemin vers l’image. La fonction load_image() regarde si le chemin qu’on lui fournit correspond à une image connue et renvoie alors une image ; sinon, elle renvoie std::nullopt, une constante magique pour créer un objet optionnel absent. La fonction try_to_load() montre comment on peut ensuite se servir d’un std::optional. Un tel objet possède un opérateur de conversion en booléen pour tester s’il contient une valeur ou pas. Alternativement, vous pouvez utilisez sa méthode has_value(), qui a une sémantique plus explicite. Une fois le test fait, la méthode value() permet de récupérer l’objet wrappé, ici une instance d’Image qui a donc une méthode getPath().

Petit détail important, l’objet wrappé est contenu dans le std::optional. Ce dernier ne contient pas un pointeur vers la valeur et aucune allocation dynamique n’est effectuée ici.

Personnellement, je trouve ça sympa 🙂

Raspberry Pi, WiFi, ssh et ligne de commande

J’ai rebranché mon vieux Raspberry Pi pour les besoins de geekeries personnelles. J’ai vu qu’il existait une version Lite de Raspbian et j’ai choisi de l’utiliser car mon Raspberry Pi n’est pas de première jeunesse et ma carte SD ne fait que 4 Go. J’ai eu une petite surprise en démarrant le système : il n’y a pas de serveur X. Il faut donc tout faire en ligne de commande. Challenged accepted!

Démarrer le Raspberry Pi

Raspbian Lite est téléchargeable sur le site de la fondation Raspberry. La version actuelle est Jessie. Je me suis aidé de deux articles que j’avais écrit il y a déjà longtemps pour créer ma carte SD avec l’image du système. J’ai branché mon clavier, ma télé, l’alimentation et j’ai obtenu prompt.

Changer le layout du clavier

Parce que qu’un système qwerty quand on a un clavier azerty c’est pas sympa, surtout si on a besoin d’utiliser des symboles, j’ai commencé par passer en azerty. Internet vous propose plusieurs techniques, j’ai utilisée celle-ci :

sudo dpkg-reconfigure keyboard-configuration

La configuration est intuitive avec une interface type ncurse. Quand c’est terminé, il suffit de redémarrer avec la commande reboot.

Configuration du WiFi

Aucune difficulté à ce que Raspbian reconnaisse mon dongle WiFi. Je l’ai branché et il est directement apparu comme wlan0 quand j’ai appelé la commande ifconfig. La partie un peu moins fun est la configuration du réseau WiFi (SSID + passphrase). La fondation Raspberry propose un tutoriel pour cela. Je crois avoir eu besoin de redémarrer à nouveau pour que ça fonctionne (en plus de la procédure expliquée dans le lien).

Configuration IP manuelle

En général, le serveur DHCP de votre box assignera toujours la même IP à votre Raspberry Pi. Il est quand même plus prudent de lui affecter une adresse IP fixe pour le retrouver facilement. Idéalement, il faudrait aussi demander à votre box de ne plus attribuer cette adresse IP par DHCP…

J’ai tout d’abord regardé quelle était mon adresse actuelle, quel masque de sous-réseau était utilisé et quelle était l’adresse de la gateway :

$ ifconfig wlan0
wlan0     Link encap:Ethernet  HWaddr 74:da:38:5d:fc:6b  
          inet addr:192.168.1.17  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: 2a01:cb05:8980:9400:944d:6692:7db0:9552/64 Scope:Global
          inet6 addr: fe80::9d35:79ba:e404:4253/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:14436 errors:0 dropped:400 overruns:0 frame:0
          TX packets:4818 errors:0 dropped:1 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:17129734 (16.3 MiB)  TX bytes:701974 (685.5 KiB)
$ ip route
default via 192.168.1.1 dev wlan0  metric 303 
192.168.1.0/24 dev wlan0  proto kernel  scope link  src 192.168.1.17  metric 303

J’ai ensuite modifié le fichier /etc/network/interfaces de manière adaptée :

cat /etc/network/interfaces
[...]

allow-hotplug wlan0
iface wlan0 inet static
    address 192.168.1.17
    netmask 255.255.255.0
    gateway 192.168.1.1
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

[...]

Activation de ssh

La dernière étape pour que mon système puisse être headless était d’activer le serveur SSH et ainsi pouvoir me connecter depuis mon ordinateur de bureau. La fondation Raspberry explique comment faire ceci dans ce tutoriel. Avec Raspbian Lite, il suffit de créer un fichier ayant pour nom ssh, à la racine de la carte SD. J’ai donc éteint mon Raspberry Pi et j’ai inséré ma carte mémoire dans mon Mac et j’ai utilisé le terminal :

$ cd /Volumes/boot/
$ touch ssh

Une fois le Raspberry Pi redémarré, vous pouvez regarder si le serveur SSH est démarré avec la commande suivante :

ps -eaf | grep ssh

Si vous un brin plus optimiste, vous pouvez essayé directement depuis votre PC :

$ ping 192.168.1.17
PING 192.168.1.17 (192.168.1.17): 56 data bytes
64 bytes from 192.168.1.17: icmp_seq=0 ttl=64 time=18.548 ms
64 bytes from 192.168.1.17: icmp_seq=1 ttl=64 time=6.127 ms
^C
--- 192.168.1.17 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 6.127/12.337/18.548/6.211 ms
imac-de-pierre:/ pierregradot$ ssh pi@192.168.1.17
The authenticity of host '192.168.1.17 (192.168.1.17)' can't be established.
ECDSA key fingerprint is SHA256:xv5CB0epvX+e8sKhgLFp2bP+1jzU8j7PzGAVAnZs8q4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.17' (ECDSA) to the list of known hosts.
pi@192.168.1.17's password: 

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Apr  3 16:22:55 2017
pi@raspberrypi:~ $ echo "Hell yeah!"
Hell yeah!
pi@raspberrypi:~ $ 

Et voilà ! J’ai débranché le Raspberry Pi de la télé, j’ai rangé mon clavier, et c’était bon !

PS : en fait, si vous avez un câble Ethernet, vous pouvez vous passer d’un clavier et d’une télé. Voici comment faire : Headless Raspberry Pi Setup.

PS : si vous branchez un câble Ethernet à votre Raspberry headless et que vous vous demandez quelle est son adresse IP, vous pouvez lire cet article : IP ADDRESS.

 

C vs C++ : initialisation des variables statiques

On lit souvent C/C++. Hé hé… S’il est indéniable que le ++ de C++ n’est pas là pour rien, il faut aussi savoir qu’il y a des différences en forme de pièges dans la partie « commune ». Ces différences sont particulièrement trompeuses quand on écrit du code C++ qu’on souhaite être compatible C. On va s’intéresser aujourd’hui à l’initialisation des variables statiques.

Voici un code C++ a priori anodin :

#include <stdlib.h>

int getValue()
{
    return rand();
}

int f(int c)
{
    static const int initial = getValue();

    if (initial != c)
        return c;
    else
        return initial;
}

int main()
{
    return f(42);
}

La ligne intéressante est la ligne 10 : on initialise une variable statique avec la valeur de retour d’une fonction.

Plaçons ce code dans un fichier main.cpp, plaçons ce fichier dans un projet Eclipse, et laçons un build :

[pre]16:52:21 **** Incremental Build of configuration Debug for project Experiences ****
Info: Internal Builder is used for build
g++ -std=c++0x -O0 -g3 -pedantic -Wall -Wextra
-c -o « src\\main.o » « ..\\src\\main.cpp »
g++ -o Experiences.exe « src\\main.o »

16:52:21 Build Finished (took 498ms)[/pre]

Renommons simplement le fichier pour forcer sa compilation en C. En effet, GCC se base sur l’extension du fichier pour savoir comment le compiler. Voici le résultat du build dans ce cas :

[pre]16:53:12 **** Incremental Build of configuration Debug for project Experiences ****
Info: Internal Builder is used for build
gcc -std=c99 -O0 -g3 -pedantic -Wall -Wextra
-c -o « src\\main.o » « ..\\src\\main.c »
..\src\main.c: In function ‘f’:
..\src\main.c:10:32: error: initializer element is not constant
static const int initial = getValue();
^

16:53:12 Build Finished (took 139ms)
[/pre]

L’erreur est claire. On pourrait soupçonner le qualificatif const mais cela ne change rien si on l’enlève. Le résultat est le même si la variable est statique au fichier au lieu d’être statique à la fonction.

Sans aller creuser dans les spécifications des langages, on se doute que C résoud la valeur d’une variable statique à la compilation alors que C++ autorise qu’elle soit résolue à l’exécution. Pour cela, le compilateur va sans doute générer un peu de code pour savoir si la variable a déjà été initialisée et l’initialiser le cas échéant (inutile de préciser que cela a un coût). On peut le vérifier en regardant le code assembleur de la fonction f() :

[pre] f(int):
0040144d: push %ebp
0040144e: mov %esp,%ebp
00401450: sub $0x18,%esp
10 static const int initial = getValue();
00401453: mov $0x40d020,%eax
00401458: movzbl (%eax),%eax
0040145b: test %al,%al
0040145d: jne 0x40148a
0040145f: movl $0x40d020,(%esp)
00401466: call 0x4014c4
0040146b: test %eax,%eax
0040146d: setne %al
00401470: test %al,%al
00401472: je 0x40148a
00401474: call 0x401440
00401479: mov %eax,0x40d028
0040147e: movl $0x40d020,(%esp)
00401485: call 0x4014bc
12 if (initial != c)
0040148a: mov 0x40d028,%eax
0040148f: cmp 0x8(%ebp),%eax
00401492: je 0x401499
13 return c;
00401494: mov 0x8(%ebp),%eax
00401497: jmp 0x40149e
15 return initial;
00401499: mov 0x40d028,%eax
16 }
[/pre]

Changeons l’initialisation de notre variable par quelque chose de constant, par exemple :

static const int initial = 666;

Voici le nouveau code assembleur, beaucoup plus simple, de la même fonction f() :
[pre] f(int):
0040144d: push %ebp
0040144e: mov %esp,%ebp
12 if (initial != c)
00401450: cmpl $0x29a,0x8(%ebp)
00401457: je 0x40145e
13 return c;
00401459: mov 0x8(%ebp),%eax
0040145c: jmp 0x401463
15 return initial;
0040145e: mov $0x29a,%eax
16 }
[/pre]

Pour avoir un code similaire en C, il faudrait implémenter une protection similaire, comme ceci :

#include <stdbool.h>
int f(int c)
{
    static int initial;
    static bool guard = false;

    if (guard == false)
    {
        guard = true;
        initial = rand();
    }

    if (initial != c)
        return c;
    else
        return initial;
}

On voit quelques inconvénients : perte de lisibilité, perte du qualificatif const, aucune garantie de thread-safety. A noter qu’en C++, le caractère thread-safe d’un tel code semble dépendre de la version du langage, comme débattu dans cette discussion stackoverflow « Is local static variable initialization thread-safe in C++11?« .

C’est tout pour aujourd’hui, vous êtes prévenus 🙂

Édition du 05/04/2017 : il s’avère en fait que la nécessité d’un initializer constant en C ne se limite pas aux variables déclarés avec le mot-clé static. Cette contrainte s’applique à toutes les variables ayant une static storage duration, comme expliqué dans cette discussion stackoverflow « Error “initializer element is not constant” when trying to initialize variable with const« . Ainsi, cette contrainte s’applique aussi aux variables globales.