Un sélecteur statique de types

Imaginons que nous souhaitions mettre en place un mécanisme qui identifie de manière statique, donc à la compilation. sans avoir recours à des macros (pas de #define) et de manière portable, le premier type entier signé dont la taille est de 4 bytes d'entre les types short, int et long. Nous souhaitons, ce faisant, que :

La technique – une alternative statique

Tel qu'exposé dans d'autres articles, il est possible de prendre certaines décisions à la compilation à l'aide de techniques de programmation générique et de métaprogrammation.

template <bool, class, class>
   struct static_if_else;
template <class SiVrai, class SiFaux>
   struct static_if_else<true, SiVrai, SiFaux>
   {
      using type = SiVrai;
   };
template <class SiVrai, class SiFaux>
   struct static_if_else<false, SiVrai, SiFaux>
   {
      using type = SiFaux;
   };

Ici, nous voulons exprimer :

Ces structures sont typiques de la métaprogrammation et constituent un cadre de base pour choisir un type de manière statique (à la compilation) sur la base d'un critère booléen connu lui aussi de manière statique. Depuis C++ 11, ce type est livré à même <type_traits> et se nomme std::conditional; nous utiliserons cette incarnation standard pour le reste du document.

L'un des opérateurs évalués à la compilation en C++ est l'opérateur sizeof, du fait que le compilateur lui-même en a besoin pour générer le code à partir du texte d'un programme. Cela tombe bien ici puisque notre critère de sélection est effectivement la taille des types impliqués.

Étant donné que nous souhaitons faire un choix parmi trois types (et empêcher le programme de compiler si aucun d'entre eux ne fait le travail), nous devons trouver moyen de tirer profit de cette structurer pour construire des alternatives statiques imbriquées.

class AucunTypeCorrespondant;

using entier32bits_t =
   typename std::conditional<
      sizeof(short)==4,
      short,
      typename std::conditional <
         sizeof(int)==4,
         int,
         typename std::conditional <
            sizeof(long)==4,
            long,
            AucunTypeCorrespondant
         >::type
      >::type
   >::type entier32bits_t;
int main()
{
   entier32bits_t var = 3;
}

Une stratégie simple est de procéder comme suit :

Un programme valide suite à ces définition serait celui proposé à droite. Ce programme utilisera le type associé à entier32bits_t choisi à la compilation par l'aternative statique définie plus haut; si le type en question est AucunTypeCorrespondant, alors main() ne compilera pas dû à sa tentative d'utiliser un type incomplet.

Raffiner légèrement le tout – une assertion statique

On peut faire encore un peu mieux en bloquant la compilation d'un programme sur une plateforme où le critère désiré (un entier signé encodé 32 bits parmi la gamme short, int, long) n'est pas rencontré sans même que le programme n'ait à utiliser explicitement ce type.

Le truc est d'avoir recours à une assertion statique. Le truc est détaillé dans un autre article mais repose essentiellement sur l'expression d'une définition de variable vide qui ne peut compiler que si une condition statique est respectée. Notez que cette technique en C++ 03 est intégrée au langage depuis C++ 11, ce qui explique l'allègement syntaxique et la distinction d'écriture ci-dessous.

C++ 03 C++ 11
template <bool>
   struct static_assert;
template <>
   struct static_assert <true>
   {
   };
namespace
{
   static_assert<
      sizeof(entier32bits_t)==4
   > type_entier_32_bits_indefini;
}
static_assert(
   sizeof(entier32bits_t)==4
   "type entier 32 bits indefini"
};

Ici, le critère visé est que la taille de entier32bits_t soit de 4 bytes, ce qui ne sera pas vrai si ce type correspond au type incomplet AucunTypeCorrespondant.

En appliquant sizeof(AucunTypeCorrespondant)==4 comme critère à une assertion statique, on obtient un programme qui ne compile que dans les cas pertinents sans avoir à payer le moindre coût en espace ou à l'exécution (les variables vides et inutilisées sont très simples à éliminer par des techniques d'optimisation banales).


Valid XHTML 1.0 Transitional

CSS Valide !