25 mar. 2008

Le futur standard C++ : nouvelle version du draft

Le standard C++ se précise davantage. Vendredi dernier, le mailing résultant de la dernière réunion du comité de normalisation a été posté sur le site officiel du workgroup. Parmi les documents les plus importants, on retrouve bien évidemment la dernière mouture du brouillon du futur standard.

Le rapport de l'éditeur (Pete Becker, auteur de The C++ Standard Library Extensions: A Tutorial And Reference, l'un des seuls livres existant sur les extensions apportées par le TR1) est édifiant : pas moins de 16 addition et modifications d'importance sont présente dans cette nouvelle version du draft. Parmi ces modifications, quelques perles qui vont modifier votre façon de penser C++ :

New Function Declarator Syntax Wording (N2541)

Par Jason Merrill.
Cette modification introduit une nouvelle syntaxe de déclaration des fonctions, reprise entre-autres dans N2550 (Lambda Expressions and Closures: Wording for Monomorphic Lambdas (Revision 4)) et N2582 (Unified Function Syntax).

Vous avez l'habitude d'écrire:

return_type function(arg_type1 arg1, arg_type2 arg2) { ... }

Cette syntaxe sera toujours disponible, mais vous pourrez aussi écrire

function(arg_type1 arg1, arg_type2 arg2) -> return_type { ... }

Le but de cette modification est d'unifier la syntaxe des déclarations de fonctions lambda et celle des fonctions et méthodes standard. N2550 est lui aussi intégré dans cette version du draft.

Lambda Expressions and Closures: Wording for Monomorphic Lambdas (Revision 4) (N2550)

Par Jaakko Jarvi, John Freeman, Lawrence Growl
D'ailleurs, en parlant de fonctions lambda : les voici qui font leur entrée dans le draft du standard, sous la forme de modifications importantes dans les chapitres 5 et 20. La proposition introduit une nouvelle classe d'expressions : les expressions lambda.

Qui n'a jamais rêvé d'écrire le code suivant :

std::vector<employee> employees;
float daily_cost = 0.0f;
std::for_each(employees.begin(), employees.end(), add_cost_to_daily_cost_only_if_i_am_a_contractor());

Jusqu'à présent, il fallait que add_cost_to_daily_cost_only_if_i_am_a_contractor() soit un objet fonction ou un pointeur sur une fonction. Il sera à l'avenir possible de définir directement une fonction en ligne, comme ceci :

std::for_each(employees.begin(), employees.end(), 
                   [&](const employee& e) { if (e.is_contractor()) daily_cost += e.daily_cost(); });

De l'utilisation de ces expressions, on observe un gain très net en expressivité. Le code exécuté est de plus directement lié à la fonction appelante, dans le sens ou les expressions lambda peuvent capturer tout ou partie du contexte dans lequel elles sont exécutée. Dans l'exemple choisi, la fonction lambda modifie une variable locale de l'appelant. C'est le mécanisme des closures. On notera aussi les différents éléments de syntaxe [ capture ](argument-list) { function-body }

  • capture est une liste de capture, qui peut être vide, et qui précise comment sont capturés les éléments de la closure (tous les éléments cités par référence, tous les éléments cités par copie, un mix des deux ou bien rien du tout).
  • argument-list et function-body sont déja relativement connus si vous utilisez le langage de temps en temps.

Le concept d'une capture est intéressant, puisqu'il permet au programmeur de contrôler la façon dont les membres de la closure sont utilisés.

  • &: les éléments de la closure sont considérés comme des références sur les éléments externes. Ce modificateur s'applique à tous les éléments ou bien seulement à certains.
  • =: les éléments de la closure sont des copies des éléments externes. Ce modificateur s'applique à tous les éléments ou bien seulement à certains.
  • (vide): pas d'éléments dans la closure.

En tant que telle, cette nouvelle fonctionnalité du langage va permettre la création d'une nouvelle génération de librairie basées sur l'utilisation d'un modèle de programmation plus proche de la programmation fonctionnelle que du modèle impératif. Une fonctionnalité à suivre donc...

STL Singly Linked Lists (N2543)

Par Matt Austern
Elles ont mis du temps à venir, mais elles sont enfin là : les listes chainées simples font leur entrée dans la librairie standard du C++. Accueillons la nouvelle venue avec le respect qui lui est du.

Concrètement, cet ajout prend la forme d'un nouveau conteneur nommé std::forward_list (anciennement std::slist). La proposition de Matt Austern fait certains choix particuliers qui sont rendus explicite par l'interface de la classe. Ainsi, l'opération d'insertion est différente de cette d'une std::list : le nouvel élément est inséré après le noeud courant plutôt qu'avant. Ce choix est illustré par le nom de la fonction d'insertion, qui devient std::forward_list::insert_after(). De même, erase() disparait au profit de std::forward_list::erase_after(). Cela peut poser des problèmes si les conteneurs sont manipulés par des fonctions template.

Autre choix important : std::forward_list ne contient pas de méthode size(). Pour trouver la taille d'une liste, il faudra utiliser l'algorithme std::distance() (un algorithme d'une complexité linéaire). L'auteur explique ce choix en deux phases :

  1. avoir une méthode size() qui fonctionnerait en O(N) serait contre-intuitif.
  2. ajouter dans la classe une variable pour stocker la taille courante (et transformer size() en une méthode de complexité constante) implique de ne pas respecter l'un des principes d'architecture du langage C++ (you pay for what you use) puisque les opérations de maintenance de cette variable ainsi que sa présence même représentent des coûts cachés pour le programmeur utilisateur.

Excision of Clause 31 (N2549)

La clause 31, c'est le chapitre 31 du dernier draft du standard. Cette clause (Date and time library) définissait les classes hour, minutes, seconds, milliseconds, etc ainsi que les traitements et opérateurs associés à ces classes. D'un point de vue des vendeurs de librairies, la multitude de classes limite de fait leur utilisation et complexifie inutilement le standard. Cette complexité n'introduit pas de fonctions supplémentaires, seulement des grandeurs. On comprends la réticence de certains à voir le comité de standardisation adopter cette clause.

Il s'est d'ailleurs passé quelque chose d'inhabituel vis à vis de cette clause : elle est très impopulaire parmi les membres du comité de normalisation, mais aucune des contre-propositions n'a été jugé satisfaisante. De fait, le comité a décidé de supprimer ce chapitre du draft, avec deux directions possibles à suivre :

  • retarder la décision de remplacer la section; dans ce cas, l'éditeur devra effectuer certains changements importants dans le texte des autres chapitres. En pire cas, on pourrait se retrouver avec un type duration non défini explicitement, mais utilisé dans de nombreuses nouvelles fonctions de la librairie standard.
  • remplacer cette section par l'élément le plus simple (probablement un simple typedef d'un type intégral en quelque chose comme std::duration_t. On perds ainsi de nombreuses fonctionnalités liées à la gestion des dates et heures.

Conclusion

Cette série, qui a pour but de vous familiariser avec les fonctionnalités du future standard C++, va continuer à s'enfoncer des les méandres des drafts successifs. Les prochains billets continueront de traiter des différentes modifications en profondeur. En attendant, je vais continuer de lire tout ça.

Je sens que je n'ai pas fini...

Commentaires

1. Le dimanche, août 3 2008, 23:17 par Alp

En effet tu n'as pas fini... Je vais essayer de couvrir un peu les nouveautés par des articles sur Developpez, mais il y a de quoi faire...

Ajouter un commentaire

Les commentaires peuvent être formatés en utilisant une syntaxe wiki simplifiée.

Fil des commentaires de ce billet