Table of Contents

Commit 2014-09-03

C++11

Suite à la mise à jour des machines linux, nous pouvons maintenant utiliser des compilateurs plus récents qui proposent des nouvelles fonctionnalités liées à la norme [http://fr.wikipedia.org/wiki/C%2B%2B11|C++11]]. Ces fonctionnalités sont, pour ainsi dire, toutes liées à une augmentation de la lisibilité du code (simplifications d'écriture) et une amélioration du typage des objets pour rendre la programmation plus sûre (conversions implicites plus difficiles).

Malheureusement, toutes les fonctionnalités C++11 ne sont pas implémentées dans les compilateurs actuels, même les plus récents (voir ici pour une comparaison VS11 et gcc). Pour Metafor, on doit donc se limiter à un sous ensemble de fonctionnalités pour rester portable. En pratique, parmi les compilateurs qu'on utilise, c'est le visual studio qui est le plus à la traîne, donc on va se limiter à ce que propose Microsoft concernant le C++11. Vous allez voir: c'est déjà pas mal du tout!

J'ai trouvé un article assez bien rédigé qui présente cet ensemble de nouveautés. Ci-dessous, je vous résume cet article dans le “cadre Metafor”:

auto

Il est devenu possible, en C++11, lors de la déclaration d'une variable, de ne pas spécifier son type. On utilise dans ce cas le mot-clef auto. Par exemple:

auto i = 0;  // i est un 'int'

Le but est de raccourcir les déclarations dont le type est très long. Petit exemple tiré de Gen4:

std::list<Node *>::iterator itn;
for(itn=line.nodes.begin(); itn!=line.nodes.end(); ++itn)
    (*itn)->container = &side;

devient

for(auto itn=line.nodes.begin(); itn!=line.nodes.end(); ++itn)
    (*itn)->container = &side;

Conclusion: dans Metafor, on pourrait utiliser auto pour déclarer des itérateurs ou des types un peu bizarres, longs à écrire (templates). Evidemment, utiliser auto pour toutes les déclarations est possible mais ce ne sera autorisé que dans Metalub!

nullptr

Le pointeur NULL peut s'écrire maintenant nullptr. C'est un objet qui a son propre type, qui n'est pas un int. Le but est de rendre la conversion NULLint plus difficile.

Conclusion: dans Metafor, tous les pointeurs NULL devraient pouvoir être remplacés par des nullptr. Seule exception: les appels vers des libs qu'on ne contrôle pas (Qt, VTK, BLAS, etc).

''begin''/''end'' non membre

Lors d'une boucle sur un container STL, Stroustrup conseille d'utiliser std::begin() et std::end() dans les arguments du for pour extraire les bornes du container. La boucle de mon exemple précédent deviendrait:

for(auto itn=std::begin(line.nodes); itn!=std::end(line.nodes); ++itn)
    (*itn)->container = &side;

Ca permet de surdéfinir std::begin et std::end pour définir des itérations plus complexes, sans pour autant dériver la classe du container.

Dans Metafor, on pourrait peut être utiliser ceci pour éliminer le code des itérateurs complexes (ElementSet p.expl.) - à regarder de plus près… plus tard.

Fonctions lambda

Comme en python, on peut écrire des fonctions lambda. Ça sert à quoi? L'utilisation la plus courante est d'éviter de créer une classe pour définir un objet-fonction qu'on ne va utiliser qu'une seule fois par la suite. Une application directe est l'utilisation des fonctions TBB pour paralléliser des boucles (le corps de la boucle peut être une fonction lambda, ce qui raccourcit l'écriture).

La boucle de l'exemple précédent devient par exemple:

std::for_each(std::begin(line.nodes), std::end(line.nodes), 
    [&](Node *n) { n->container = &side; });

Dans Metafor, ces fonctions lambda vont permettre de nettoyer la plupart des boucles de type std::for_each qui utilisent aujourd'hui des atrocités du type std::bind2nd(std::mem_fun_ref(…) et les boucles TBB.

Range-based for (boucle "à la python")

Après vous avoir expliqué comment utiliser proprement et élégamment des itérateurs, je vous explique maintenant qu'il est possible, en C++11, de simplement écrire des boucles comme on le fait en python. L'utilisation des itérateurs est alors implicite.

Le code de ma boucle-exemple devient ridiculement simple et lisible par un débutant:

for(auto n : line.nodes)
    n->container = &side;

J'espère que cette dernière manière d'écrire les boucles en C++ vous aura convaincu que l'utilisation de la nouvelle norme apporte quelque chose. Si on veut résumer en une seule phrase l'intérêt, on peut dire que les nouvelles fonctionnalités du C++ qui ont été retenues ici permettent d'utiliser la STL de manière simple et claire, ce qui n'était pas le cas avec l'ancienne norme où les itérateurs apparaissaient explicitement dans le code.

Autres

Je n'ai pas encore testé d'autres fonctionnalités, mais je vais le faire. Voici ce qui me semble utile à première vue parmi ce que propose le VS11 (le C++11 est bien plus que ça!):

  1. Les énums fortement typées: il s'agit d'enums qui ne peuvent pas être converties en int. De plus, les valeurs sont définies sous la forme nom_de_l_enum.VALEUR au lieu de valeur. Bref, comme si on mettait l'énum dans une classe ou un namespace. Je dois vérifier que SWIG les digère bien avant de les utiliser en masse dans le code.
  2. Les constructeurs move: ce sont des nouveaux constructeurs qui permettent par exemple de retourner des objets locaux sans effectuer une copie. Ca permet aussi de gérer efficacement les objets temporaires (on parle de “rvalues”), c'est-à-dire des objets auxquels on ne peut rien assigner et qui sont sur le point d'être détruits. L'idée est de définir un constructeur par copie qui repose sur l'hypothèse que l'objet à copier va être détruit juste après… on peut donc par exemple lui voler la mémoire qu'il a alloué. Application évidente à Metafor: ne refonte des objets Matrix où on pourrait éliminer la gestion pourrie de la mémoire liée aux objets temporaires. C'est un exercice que j'aimerais faire.
  3. Les static_assert: des assertions version C++. J'ai l'impression que c'est surtout utile pour vérifier des types lors de l'instanciation de templates… Pourquoi pas…
  4. Les smart_pointer (unique_ptr<>, shared_ptr<>, etc): des “vrais” pointeurs intelligents qui permettraient peut-être de se passer à terme des compteurs de référence home-made de Metafor.

Quand on passera au VS12 (Visual Studio 2013), on pourra également utiliser:

  1. les arguments de templates “par défaut”: ca permet de définir simplement des arguments par défaut dans un but, à nouveau, de simplifier l'écriture.
  2. les initialiseurs par listes: imaginez initialiser un std::vector<int> par = {1, 2, 3, 4}, comme vous le feriez avec un int[4]… c'est beau et ce sera possible.

Gen4

TBB

StrVectorBase.useTBB() # assemblage des vecteurs en parallèle
StrMatrixBase.useTBB() # assemblage des matrices en parallèle

Parallélisation ALE

Post Matlab

Divers

Linuxades

Pour rappel, je travaille maintenant sous Linux. Le but étant de faire du parallèle beaucoup plus facilement que sous Windows et bénéficier des packages de libs bien ficelés fournis avec le système. Installer le système et compiler Metafor à partir de rien prend environ 3 heures avec Linux (comptez 2 jours pour Windows, si Luc ne vous passe pas ses libs). Metafor sous Linux est aussi environ 10% plus rapide que sous Windows.

Revers de la médaille… c'est linux: pas de Corel, pas de Photoshop, ni d'Office… Néanmoins, je crois maintenant avoir trouvé un substitut/une alternative +/- valable pour tous les programmes couramment utilisés. Dans le pire des cas, j'utilise une virtualbox (d'autres installent des programmes windows avec Wine… j'ai évité jusqu'à présent).

Bref, si vous vouliez migrer un jour du côté fuck bilou de la force, la route est déjà balisée. Je peux vous donner des tuyaux.

Romain BOMAN 2014/09/03 09:34