lundi 6 août 2012

User-defined literals

Plongeons immédiatement dans une fonctionnalité nettement plus discutable de C++11, les "User-defined literals". Vous souvenez-vous du petit "f" à la fin des nombres pour indiquer qu'il s'agit d'un flottant? Eh bien, ceci est la généralisation du concept. L'idée est de fournir la possibilité de créer des opérateurs qui travaillent sur certains types de base pour retourner un type défini par l'utilisateur, via un suffixe. Démarrons tout de suite avec un exemple: définissons un nouveau suffixe _str qui transforme une chaine vers un std::string.

#include <string>
#include <iostream>

std::string operator "" _str(const char * str, size_t length)
{
  return std::string(str, length);
}

int main()
{
  std::cout << "abc"_str + "def"_str << std::endl;
  return 0;
}

En définissant l'opérateur "" suivi du suffixe choisi, l'on peut maintenant transformer nos chaînes en std::string directement, nous permettant d'utiliser un + qui aurait sinon fait hurler le compilo. Notons qu'il est possible de faire à peu près n'importe quoi dans l'opérateur "", comme de bidouiller la chaîne, ou d'en retourner la longueur, ou quoi que ce soit d'autre. Le type de retour est librement défini par l'utilisateur.

La liste des types que peut prendre l'opérateur "" en paramètre est la suivante:

  • char const *, pour les nombres sous forme de chaîne
  • unsigned long long int, pour les entiers
  • long double, pour les flottants
  • char const *, size_t, pour les chaînes
  • wchar_t const *, size_t, pour les chaînes de caractère wide
  • wchar16_t const *, size_t, pour les chaînes de caractères 16 bits
  • wchar32_t const *, size_t, pour les chaînes de caractères 32 bits
Petites explications supplémentaires sur le premier élément: il s'agit de lire un nombre directement sous forme de chaîne, le compilateur s'abstenant alors d'en faire la transformation préalable vers un entier. Cet opérateur peut être utile si l'on veut lire de très longs entiers, par exemple dans une bibliothèque de calcul en précision infinie. Voyez l'exemple suivant:

#include <string>
#include <iostream>
#include <sstream>

std::string operator "" _miaou(const char * str)
{
  return("***" + std::string(str) + "***");
}

std::string operator "" _ouaf(unsigned long long int i)
{
  std::ostringstream str;
  str << i;
  return "***" + str.str() + "***";
}

int main()
{
  std::cout << 1234567890123456789012345678901234567890_miaou << std::endl;
  std::cout << 1234567890123456789012345678901234567890_ouaf << std::endl;  

  return 0;
}

À l'exécution, s'affiche:

***1234567890123456789012345678901234567890***
***12446928571455179474***

Les plus observateurs d'entre vous auront remarqué que les opérateurs ne prennent pas d'entiers négatifs. En effet, -123_miaou est interprété comme operator-(operator "" _miaou(123)). Il faudra donc redéfinir operator- si vous voulez retourner un type bizarre.

J'espère sincèrement que cette fonctionnalité va rester confidentielle, car elle me semble un peu trop facile à abuser. Cependant, je ne demande qu'à être convaincu: une bibliothèque physique permettant d'utiliser les unités SI en suffixe pourrait peut-être permettre d'écrire du code fortement typé qui reste très lisible. À voir.

Aucun commentaire: