Un trait pour le type d'une donnée

Cet article présume que vous êtes familière ou familier avec les traits (sinon, il est possible que vous ne voyez pas l'intérêt de ce qui suit). Notez que nous utiliserons des éléments syntaxiques de C++ 11.

Un brillant étudiant de la cohorte 07 du DDJV, Kenzo Lespagnol, m'a demandé comment il pourrait déterminer un trait représentant le type d'une donnée, par exemple l'attribut x d'un D3DXVECTOR3 (un type important de la bibliothèque DirectX).

Mon réflexe fut le suivant :

  • Supposons un type VEC3, qui ressemble (de manière très simplifiée) au type qui nous préoccupe
  • Définissons ce type tel que proposé à droite, pour circonscrire le problème et ne pas s'empêtrer dans l'installation d'une bibliothèque graphique 3D quand, au fond, notre problème n'en dépend pas
struct VEC3
{
   float x, y, z;
   // bla bla
   VEC3(float x, float y, float z)
      : x{x}, y{y}, z{z} // oui, cette syntaxe est légale!
   {
   }
};

J'ai choisi ici d'écrire un constructeur paramétrique pour effacer le constructeur par défaut. Ceci nous forcera à trouver une solution à peu près universelle, au sens où elle ne présumera que le constructeur de copie chez VEC3. Ainsi, pas question de créer un VEC3 vide et de travailler sur son attribut x, puis d'en arriver à une solution qui ne fonctionne que pour les types qui peuvent justement être construits par défaut. De telles solutions sont trop restrictives en pratique.

J'ai plutôt choisi de déclarer la fonction creer_vec3() retournant un VEC3 par copie.

Notez que je ne définirai pas cette fonction. Elle est tout à fait hypothétique : c'est une fonction qui, si on l'appelle un jour (ce que nous ne ferons pas!), retournera un VEC3.

VEC3 creer_vec3();

Travailler ainsi nous permet de raisonner dans l'abstrait, au sens où tout ce qui mène à l'instanciation d'un hypothétique VEC3 est omis, escamoté, et nous pouvons ainsi nous concentrer sur un raisonnement basé sur les types, pas sur les objets. C'est une démarche raisonnable, les types existant en tant que tels à la compilation seulement (du moins en C++).

Pour ce qui est du trait recherché, voici le raisonnement que j'ai suivi :

  • Supposons qu'on appelle creer_vec3(), ce que l'on ne fera pas, je le rappelle
  • Quel serait le type de l'expression .x appliquée à ce que cette fonction aurait, hypothétiquement, retourné?

Il existe des techniques de métaprogrammation pour répondre à cette question, mais C+ 11 offre l'opérateur statique decltype(expr) pour représenter le type de l'expression expr. Ainsi, nous pouvons définir le trait vec3_traits::value_type correspondant un type de l'attribut x d'un VEC3 comme proposé à droite.

struct vec3_traits
{
   using value_type = decltype(creer_vec3().x);
};

Muni de ce trait, il est par exemple possible de définir une variable du même type que le type de l'attribut x d'un VEC3, comme le démontre le programme tout simple proposé à droite.

int main()
{
   vec3_traits::value_type val = 3.14159f;
}

Et voilà!


Valid XHTML 1.0 Transitional

CSS Valide !