Pages

mercredi 27 février 2013

Faites parler votre compilateur

Voici la liste de mes alias pour compiler des codes C, C11, C++ et C++11 et ainsi éviter de nombreuses erreurs de codage et avoir un maximum d'info sur les potentielles erreurs. Les flags les plus importants étant quand même -Wall, -Wextra et -Werror pour transformer les avertissements en erreurs.

alias gcc1x='gcc -std=c1x'
alias g0x='g++ -std=c++0x'
alias g11='g++-4.7 -std=c++11'

alias colorgcc1x='colorgcc -std=c1x'
alias colorg0x='colorg++ -std=c++0x'
alias colorg11='colorg++-4.7 -std=c++11'

flag='-Wall -Wextra -Wundef -Wcast-align -Wformat-security -Wunreachable-code -Wformat=2 -Werror-implicit-function-declaration -Wfloat-equal -Wshadow -Wpointer-arith -Wconversion -Wmissing-declarations -Wmissing-noreturn -Wmissing-format-attribute -Wpacked -Wredundant-decls -Winline -Wdouble-promotion -Winit-self -Wcast-qual -pedantic'
cflag=$flag' -Wstrict-prototypes -Wbad-function-cast -Wmissing-prototypes -Wnested-externs -Waggregate-return -Wwrite-strings'
cppflag=$flag' -Wold-style-cast -Woverloaded-virtual -Wnon-virtual-dtor'
unset flag
alias gwcc="gcc $cflag -Wlong-long"
alias gwcc1x="gcc $cflag -std=c1x"

alias colorgwcc="colorgcc $cflag -Wlong-long"
alias colorgwcc1x="colorgcc $cflag -std=c1x"

unset cflag

alias gw++="g++ $cppflag -Wlong-long"
alias gw0x="g++ $cppflag -std=c++0x"
alias gw11="g++-4.7 $cppflag -std=c++11"

alias colorgw++="colorg++ $cppflag -Wlong-long"
alias colorgw0x="colorg++ $cppflag -std=c++0x"
alias colorgw11="colorg++-4.7 $cppflag -std=c++11"

unset cppflag

alias gfcc='gcc -Wfatal-errors'
alias gf++='g++ -Wfatal-errors'
alias gfcc1x='gcc1x -Wfatal-errors'
alias gf0x='g0x -Wfatal-errors'
alias gfwcc='gwcc -Wfatal-errors'
alias gfwcc1x='gwcc1x -Wfatal-errors'
alias gfw0x='gw0x -Wfatal-errors'

alias colorgfcc='colorgcc -Wfatal-errors'
alias colorgf++='colorg++ -Wfatal-errors'
alias colorgfcc1x='colorgcc1x -Wfatal-errors'
alias colorgf0x='colorg0x -Wfatal-errors'
alias colorgfwcc='colorgwcc -Wfatal-errors'
alias colorgfwcc1x='colorgwcc1x -Wfatal-errors'
alias colorgfw0x='colorgw0x -Wfatal-errors'

Ma version de gcc et la 4.6 par défaut mais la 4.7 (que j'ai aussi) a un meilleur support du C++11.

colorgcc se trouve dans les paquets (d'ubuntu) mais il faudra un patch pour avoir colorg++ (facilement trouvable sur le net).

Je compile généralement mes tests en ajoutant -Wno-missing-declarations pour permettre d'éliminer les warnings lorsque la fonction n'a pas de prototype ou n'est pas inline.

vendredi 22 février 2013

Nouveauté sur les unions

Avant le C++11 les types mis dans les unions se limitaient aux objets POD.
Maintenant plus de limite, faut juste bien appeler le destructeur de l'objet construit dans l'union au risque de jolie fuite mémoire :).

union U{
  int i;
  std::string s;

  U():i(){} //constructeur obligé car s n'est pas un objet POD
  ~U(){} //destructeur qui ne fait rien mais aussi obligé

  void construct_s(const std::string& str)
  { new (&s) std::string(str); } //inclure <memory>

  void destroy_s()
  { s.~basic_string(); } //note string = basic_string<char>
};

Comme tel, c'est un peu limité mais c'est pour montrer...

dimanche 17 février 2013

Différence entre $@, $*, "$@" et "$*"

Après avoir vu l'utilisation de $@ dans un script shell, je me suis demandé la différence avec $*.
Pour rappel, ces variables représentent les arguments de la ligne de commande.

$* est une variable ce qu'il y à de plus normale et ne diffère pas d'une autre variable. Cependant, le comportement des variables diffère en fonction du shell, notamment sur zsh (j'y reviens après).
$@ est une variable au comportement différent entre les shells basés sur sh (bash,dash,...) et les autres (ksh, zsh).

Sur sh aucune différence entre $@ et $*. Par contre sur ksh/zsh cela représente le `tableau` d'argument. Il faut savoir qu'une variable sans guillemet revient à créer autant d'arguments qu'il y a de mots. Les mots sont séparés en fonction des caractères de $IFS (la variable contient : espace, tabulation et saut de ligne). C'est pour ça qu'il est conseillé d'entourer ces variables de guillemets doubles.

Un petit exemple pour comprendre :).

$ a='a b c'; for v in $a ; do echo $v ; done
> a
> b
> c

Le résultat est 3 lignes pour 1 paramètre, la chaîne 'a b c' contenant des espaces s'est fait couper. Ce n'est pas le cas pour zsh (raison historique, j'y reviens à la fin).

Maintenant il reste "$*" et "$@" qui ne diffère pas entre les shells.
"$*" correspond à une seul chaîne, tout est gérer en un seul bloc.
"$@" représente les paramètres réel. C'est identique à $@ avec ksh et zsh.

Petit tableau récapitulatif avec le même type de boucle qu'au-dessus et un appel avec deux paramètres.

./test 'a b' c
var/shell sh/bash ksh zsh
$*
> a
> b
> c
> a
> b
> c
> a b
> c
$@
> a
> b
> c
> a b
> c
> a b
> c
"$*"
> a b c
> a b c
> a b c
"$@"
> a b
> c
> a b
> c
> a b
> c

Pour que zsh boucle sur des mots, il faut ajouter un flag à la variable (${=*}), comme ci-dessous.

$ a='a b c'; for v in ${=a} ; do echo $v ; done

mercredi 6 février 2013

Appel explicite aux membres d'une classe

Ce qui suit permet d'appeler les attributs/fonctions cachés du fait d'un héritage ou faire appel à la fonction virtuelle d'un parent même si celle-ci est redéfinie dans un fil.

Ce principe est plus connu à l'intérieur d'une classe est utilisé avec l’héritage:

struct A
{
 virtual int f() { return 3; }
 virtual ~A(){}
};

struct B : A
{
 virtual int f()
 { return A::f() + 3; } // Utilise la fonction f() du parent
};

Mais il peut aussi s'utiliser à l'extérieur d'une classe. Le A::f() fait référence à une fonction de l'objet lié au pointeur this. Donc this->A::f() est syntaxiquement valide. Ce qui veut dire qu'on peut faire référence à n'importe quel membre contenu dans l'objet.

struct A { int n = 2; };
struct B : A { int n = 3; };

B b;
b.n == 3
b.A::n == 2
struct C { virtual int f(){ return 2; } };
struct D : C { int f(){ return 3; } };

D d;
b.f() == 3
b.C::f() == 2

Cette technique est utilisé pour par exemple changer le buffer d'un fstream. La méthode std::ios::rdbuf(std::streambuf*) étant cacher par std::ofstream::rdbuf() c'est un moyen plus simple que le cast en une référence sur std::ios.

std::ofstream os("...");
//...
static_cast<std::ios&>(os).rdbuf(buf);
//ou
os.std::ios::rdbuf(buf);