Pages

mardi 25 décembre 2012

La vis cacher de getopt

Voici une petite information très mal connue et peu utilisée du getopt de la lib C et de la commande shell. Ainsi que de Boost.Program_options (parce que boost c'est bien ^^).

Les noms des options longues n'ont pas besoin d'être écrites entièrement.

$ getopt -o '' --long \
option-longue,option-encore-plus-longue,une-autre-option: \
-n 'example' -- \
--option-l --u plop bidule
> --option-longue --une-autre-option 'plop' -- 'bidule'

La commande shell getopt est un peu plus souple que les autres, si ambiguïté la première option correspondante sera sélectionnée. Si l'option -a existe ce n'est plus le cas et le code d'erreur 1 est retourné ainsi qu'un petit message listant les options possibles. Mais avec -a les options longues peuvent commencer par un simple tiret.

La plupart des commandes Linux utilisant getopt, cette astuce peut s'utiliser assez souvent.

jeudi 20 décembre 2012

Taguer vos classes, cataloguées les

Le C++ a l'avantage de faire de la surcharge de fonction et permet ainsi de spécifier des algorithmes selon des critères> Ici ce seront des `tags`.

Comme exemple je vais utiliser les tags présents dans les itérateurs de la stl et une implémentation de la fonction std::advance().

Première implémentation

La fonction std::advance() permet d'incrémenter un itérateur de N éléments (ou décrémenter si N est négatif). D'après cette description, un premier algorithme peut être émis:

template<class InputIt, class Distance>
void advance(InputIt& it, Distance n)
{
  if (n < 0){
    while (n++)
      --it;
  }
  else {
    while (n--)
      ++it;
  }
}

Optimisation

Maintenant que se passe-t-il quand l'itérateur est un pointeur ? Réponse: Une boucle.
Ne serait-il pas préférable de faire it += n !?
C'est là que les tag rentrent en jeux.

Utilisation des tags

Les itérateurs possèdent des types prédéfinis qui sont: pointer, value_type, reference, difference_type et iterator_category. C'est ce dernier qui correspond au tag de l'itérateur.

En réalité un tag est une classe vide, cela suffit, le type est porteur de l'information. Les tags des itérateurs possèdent aussi une hiérarchie et certains sont donc hérités.

Pour récupérer le type contenu dans un itérateur il faut passer par std::iterator_traits<>, cela permet de fonctionner même avec un type scalaire comme le pointeur qui ne possède pas de type interne.

Le tag d'un pointeur étant std::random_access_iterator_tag voici une implémentation:

template<class It>
class std::iterator_traits<It>::iterator_category
iterator_category(const It&)
{ return typename std::iterator_traits<It>::iterator_category(); }

template<class RandomIt, class Distance>
void advance(RandomIt& it, Distance n, std::random_access_iterator_tag)
{ it  += n; }

template<class InputIt, class Distance>
void advance(InputIt& it, Distance n)
{ advance(it, n, iterator_category(it)); }

Bien sûr, on change aussi le prototype de la précédente implémentation.

template<class InputIt, class Distance, class Tag>
void advance(InputIt& it, Distance n, Tag);

Problème avec ForwardIterator

Toutefois un problème persiste, lorsqu'un ForwardIterator est utilisé, il n'y a pas d'opération de décrémentation, et la compilation ne se fait pas. Il faut de nouveau changer notre première implémentation pour qu'elle ne fonctionne qu'avec bidirectional_iterator_tag et une implémentation généraliste qui fait une incrémentation.

template<class InputIt, class Distance, class Tag>
void advance(InputIt& it, Distance n, Tag)
{
  while (n--)
    ++it;
}

template<class InputIt, class Distance>
void advance(InputIt& it, Distance n, std::bidirectional_iterator_tag)
{
  if (n < 0){
    while (n++)
      --it;
  }
  else {
    while (n--)
      ++it;
  }
}

Conclusion

L'utilisation de tag combinée avec la surcharge de fonction est plus simple que la spécialisation de template et permet un meilleur ciblage dans les algorithmes utilisés.

Good conclusion :D

samedi 15 décembre 2012

Les littéraux

En C++11 le langage introduit les littéraux définis par l'utilisateur. Pour rappel, les littéraux permettent de forcer un type via un suffixe, comme avec 1L pour créer un long.

Les littéraux se définissent par un nouvel opérateur: operator "", et le nom du suffixe (identifiant) doit obligatoirement être précédé d'un underscore. Les suffixes sans underscrore sont réservés par les standards existants ou à venir.
À noter qu'un espace -ou saut de ligne- est obligatoire après "".

Il existe deux types de littéraux, ceux sur des nombres et ceux sur des caractères.

Littéraux sur nombres

result_type operator "" _suffix(unsigned long long); // 334_suffix
result_type operator "" _suffix(double long); // 334.2_suffix
template<char...> result_type operator "" _suffix(); // 334_suffix ou  334.2_suffix
result_type operator "" _s(const char *); // 334_suffix ou  334.2_suffix

Quatre prototypes, le premier s'utilise sur des entiers, le second sur des nombres flottants, les troisième et quatrième pour entiers ou flottants et sont cachés par les 2 premiers si définis.

L'overflow

Le problème est que le paramètre peut être plus grand que le contenu du type de retour.
Par exemple, si _suffix retourne un short alors 99999_suffix doit générer une erreur de compilation car la valeur est trop grande.
En utilisant les 2 premiers prototypes, cette vérification ne peut pas se faire, le paramètre d'une fonction n'étant pas considéré comme const-expressif, static_cast ne peut pas être utilisé dessus et donc: pas de vérification à la compilation.
Le troisième prototype est le seul moyen de gérer l'overflow. Cependant son utilisation peut devenir complexe…
J'intégrerai bientôt dans falcon/literal une classe pour simplifier cette vérification ;).

Littéraux sur caractères

result_type operator "" _suffix(char); // '2'_suffix
result_type operator "" _suffix(const char *, std::size_t len); // "334"_suffix

Un prototype pour les caractères et un autre pour les chaînes. Ce dernier prend la taille de la chaîne en second paramètre.
À noter que char peut être remplacé par wchar_t, char16_t ou char32_t, ce qui fait un total de 8 prototypes.

Un cas d'utilisation simple est la concaténation de chaîne std::string("text: ") + str qui peut être simplifier en "text: "_s + str

std::string operator "" _s(const char * s, std::size_t len)
{ return std::string(s, len); }

Pour finir

Je vous propose d'aller jeter un œil ou deux dans falcon/literal (sans vouloir insister :o)).
En cadeau la liste des littéraux prédéfinis dans le standard:

lundi 10 décembre 2012

Les lambdas en C++

Les lambdas ou fonctions anonymes sont un mécanisme présent dans beaucoup de langages de programmation, C++11 en fait partie :).

Le principe est simple, créer un foncteur à la volée pour le passer directement à une fonction/classe ou le stocker dans une variable. Par exemple, dans mon précédant billet sur le C++, la variable f contient une lambda.

La plus courte et inutile lambda existante fait 4 caractères, ce sont les 4 caractères obligatoires à la création d'une lambda : []{}

La syntaxe complète est celle-ci :
[clause-de-capture](paramètres...) mutable-specification exception-specification -> type-de-retour { corps-du-lambda }
Ce qui fait 6 parties

Clause de capture

[clause-de-capture]: spécifie comment l'expression lambda accède aux variables dans la portée englobante. Par défaut, aucune variable n'est accessible.
Un & indique un accès par référence et un = par valeur.
Si la & est préfixée d'un nom de variable, alors seule cette variable est accessible par référence.
Si le nom de variable n'est pas préfixé alors la variable est accessible par valeur.
Pour finir, la clause this permet d’accéder au scope de la classe (si créé dans une classe, évidemment).
Bien sûr, plusieurs clauses peuvent être indiquées en les séparant par des virgules.

  • [=] tous par valeur
  • [&] tous par référence
  • [x] x par valeur
  • [&x] x par référence
  • [&, x] x par valeur, tout le reste par référence
  • [=, &x] x par référence, tout le reste par valeur
  • [this] permet d'utiliser le pointeur this

Liste de paramètres

(paramètres...): Pareil que pour une fonction mais pas de template. En c++14, les types peuvent être auto pour remplacer les templates.
Facultative si mutable-specification, exception-specification et type-de-retour ne sont pas indiqués.

Spécification mutable

mutable: Permet de modifier les variables qui sont capturées par valeur.

[=](){++x} // erreur car x est en lecture seule
[=]() mutable {++x} // ok

Spécification d'exception

noexcept: Là rien d'extraordinaire, on l'indique si aucune exception n'est retournée.

Type de retour

-> type-de-retour: Indique le type de retour. Optionnel, le compilateur utilisant automatiquement un decltype.

Corps du lambda

{ corps-du-lambda }: le super algorithme de la mort qui tue.
À noter que les variables globales et statiques sont accessibles.

mercredi 5 décembre 2012

setTimeout/setInterval et le eval caché

Tout le monde sait que eval c'est le mal, surtout parce que les interpréteurs (ou compilateurs) n'optimisent pas les codes générés avec.

Il existe au moins deux fonctions faisant un eval() si mal utilisées: setTimeout et setInterval.

Ces deux fonctions peuvent prendre une chaîne de caractères, et si c'est le cas, le mal, euuh eval est enclenché.

Une dernière chose mal connue car n'existant pas sur IE (jusqu'à je ne sais quelle version) est que les paramètres supplémentaires seront transmis à la fonction lors de son appel.

setTimeout(alert, 1000, "plop") //alert("plop") au bout d'une seconde

Et pour la perte du this (qui se présente toujours lors de passage de fonction), il y a Function.bind depuis js 1.8.5. Comme souvent (toujours ?) une implémentation est présente pour les navigateurs ne l'implémentant pas encore.