lundi 29 septembre 2014

Gcc prend ses aises

J'ai récemment ressorti OpenRailz de mes cartons, pour voir s'il compilait encore. Je lance donc comme d'hab ma commande omake -j 4. Et là, c'est le drame: en quelques minutes, ma machine est à genoux, swappe à n'en plus finir, le pointeur de souris devient saccadé... Je tue la compile, tout redevient normal. Je recommence, même punition mêmes motifs.

En y regardant de plus près, je me rends compte que chaque compilation mange à peu près 1Go de RAM. Avec 4 compilations en parallèle, c'est fini, mes 4 Go sont partis. Et tout ce qui a changé depuis la dernière fois où je compilais joyeusement cette solution, c'est la version du compilateur.

J'avais en effet entendu dire que particulièrement depuis gcc 4.8, la consommation mémoire s'était accrue, mais là, je l'ai senti en plein.

On vivra avec, parce que les fonctionnalités valent le coup. Mais moi qui me pensais à l'abri du besoin, me voilà lorgnant vers une petite mise à jour de ma RAM...

Coder efficacement avec Emacs - Sommaire et conclusion

Je remets ici la liste complète de mes tutoriels sur l'utilisation d'Emacs comme environnement de développement.

Je continue à être tout à fait satisfait de mon environnement Emacs, et sous Linux du moins, je ne me vois pas en changer. Il y a probablement moyen d'améliorer encore sa productivité, par exemple en utilisant les ctags pour faire de l'autocomplétion, mais celle-ci me semble peu utile tant qu'elle n'est pas contextuelle, et le bon vieux M-Esc / est bien plus simple pour un résultat presque aussi bon. Peut-être que la compétition renouvelée entre gcc et clang pourra nous donner de meilleurs outils d'autocomplétion ou de compilation à la volée qui s’intégreront avec Emacs. En attendant, je me contente très bien de ce que j'ai. Et le regard d'envie de mon vimeux de voisin lorsque je lance un gdb-many-windows n'a pas de prix :)

samedi 27 septembre 2014

Encore de la move semantics

J'avais déjà longuement parlé des coûteuses erreurs d'inattention liées à la move semantics lors du passage de paramètres, lorsque des objets qui paraissent parfaitement valides ne le sont plus. Je me suis fait avoir une fois de plus avant-hier. Ce n'est pas aussi terrible que la fois précédente, puisque mon test unitaire a très vite attrapé la faute, mais cela prouve qu'il est finalement assez difficile de ne pas se tromper.

J'ai donc commencé à implémenter l'idée initiale proposée par Yakk sur StackOverflow, et je nomme mes arguments abcMoved pour bien indiquer qu'il faut bien faire attention:

class Person
{
 public:
  A(std::string nameMoved, std::string addressMoved):
    _name(std::move(nameMoved)),
    _address(std::move(addressMoved)
  {
    std::cout << _name << " " << _address << std::endl;
  }

 private:
  std::string _name;
  std::string _address;
};

Avec cette manière de faire, un std::cout << nameMoved << std::endl devrait nous sauter au visage. J'essaie, et je vous en recause.

lundi 22 septembre 2014

Delegating constructors

Aujourd'hui, j'ai collé mon premier delegate constructor dans du code de production.

Jusqu'ici, je n'en avais pas vraiment ressenti le besoin: la grande majorité de mes classes n'a qu'un seul constructeur, et celles qui en ont plus ont généralement besoin de comportements très différents. Mais aujourd'hui, j'ai voulu utiliser un delegate constructor, pour rendre mon code plus facile à utiliser depuis les tests unitaires.

Dans mon cas, j'avais un constructeur qui prenait, entre autres, un gros objet, à partir duquel on retirait plusieurs petits pour les mettre dans des attributs. Le problème, c'est que ce gros objet est difficilement moquable, alors que les petits le sont facilement. De plus, je veux vraiment passer le gros objet (et pas les petits) par le constructeur, pour éviter que l'utilisateur ne s'emmêlle les pinceaux sur la manière d'extraire les petits.

Ma solution fut donc de créer un constructeur protégé qui prend les petits objets, appelé par mon constructeur principal public qui prend le gros. Pas de risques de mal utiliser la classe. Et depuis mes tests unitaires, je dérive mon objet, rend l'autre constructeur visible, et passe mes objets moqués.

C'est probablement un cas d'utilisation très particulier. Je ne pense pas avoir à réitérer l'expérience de si peu. Mais il est plaisant de trouver des cas d'utilisation qui rendent le code vraiment plus élégant.

samedi 30 août 2014

Grosse mise à jour Debian

Ça occupe! Tout d'abord, Debian est passé à Postgresql 9.4. Depuis 9.0, Postgresql fait tout ce que je veux, je n'attendais pas particulièrement cette dernière version, mais ça fait toujours plaisir d'être à jour. La mise à jour est toujours aussi triviale, et mes vieilles notes toujours d'actualité. Il faut juste ne pas se tromper de cluster.

Ensuite, quelques changements mineurs dans Médoc, avec Eliom qui passe en 4.0, et change quelques noms de module, et une en-tête manquante dans le client C++. Le commit est donc trivial.

Étrangement, sur ma machine virtuelle au boulot, la mise à jour a voulu passer à systemd, mais pas à la maison. Le passage à systemd semble s'être bien passé, mais un souci avec les "Guest Additions" de VirtualBox m'empêche d'en profiter. Je continuerai donc à utiliser notre bon vieux sysV pour un moment.

jeudi 28 août 2014

Lambdas et capture d'attributs

J'ai aujourd'hui découvert que la capture d'attributs dans une lambda peut parfois donner des résultats surprenants. Regardez donc l'innocent programme que voilà. L'idée est d'avoir une classe A qui créé une lambda, laquelle capture un attribut de type pointeur partagé. L'on s'assure que A est ensuite détruit, avant d'exécuter la lambda. Que se passe-t-il?

#include <iostream>
#include <functional>
#include <memory>

class C
{
public:
  C():
    _value("abcdef")
  {
    std::cout << "Constructor" << std::endl;
  }

  ~C()
  {
    std::cout << "Destructor" << std::endl;
  }
  
  std::string _value;
};

class A
{
public:
  A():
    _c(std::make_shared<C>())
  {
  }
  
  std::function<void()> makeLambda()
  {
    return [=]()
    {
      std::cout << "Executing the lambda" << std::endl;
      std::cout << _c->_value << std::endl;      
    };
  }
  
  std::shared_ptr<C> _c;
};

int main()
{
  std::function<void()> f;

  {
    A a;
    f = a.makeLambda();
  }
  
  f();
  
  return 0;
}

Je pensais simplement que l'attribut allait être capturé par valeur (puisqu'il y a le "=" dans ma lambda), et que donc le pointeur partagé serait copié. Or, voilà ce qui se passe lorsque l'on exécute le programme:

Constructor
Destructor
Executing the lambda

L'objet C est détruit avant l'exécution de la lambda, c'est à dire au moment de la destruction de A! C'est en plein dans le royaume de l'undefined behaviour, en l'occurence ne rien afficher, mais plus probablement un crash. En cherchant un peu, je me suis rendu compte que les attributs sont en fait capturés via le pointeur this. D'ailleurs, la lambda refuse de compiler si l'on capture explicitement _c: [_c](){...};. En revanche, le compilateur est heureux en capturant this: [this](){...};.

La solution pour faire fonctionner l'exemple est tout simple: il faut copier _c dans une variable locale à la fonction, et capturer cette variable.

  std::function<void()> makeLambda()
  {
    auto localC = _c;
    return [=]()
    {
      std::cout << "Executing the lambda" << std::endl;
      std::cout << localC->_value << std::endl;      
    };
  }

Avec cette version, le résultat est celui attendu:

Constructor
Executing the lambda
abcdef
Destructor

Attention donc à ce qui est réellement capturé!

jeudi 21 août 2014

C++14 sort de sa boite

C'est fait! À l'unanimité, le nouveau standard C++14 a été approuvé, et sera rapidement officialisé. L'annonce est parue sur le blog de l'isocpp, et a fait l'objet de jolis trolls sur Slashdot. Le C++ continue, 30 ans après, de déchaîner les passions!

Quant à moi, j'espère bientôt voir apparaître dans nos compilateurs préférés les fonctionnalités de C++17, que je me ferai un plaisir de décortiquer ici. Restez branchés!