Chromium Chronicle #24: StrongAlias, IdType et TokenType

Épisode 24:de Łukasz Anforowicz à Bellevue, Washington (août 2021)
Épisodes précédents

Pouvez-vous repérer le bug dans le code ci-dessous ? Voyez-vous le bug dans un examen de code, si vous n'examinez que le site d'appel ?

Token CreateToken(int command_data, int buffer_id);
...
auto token = CreateToken(GetCommandBufferId(), GetCommandData());

Un même type peut parfois représenter des valeurs provenant de domaines incompatibles. Cela se produit généralement pour les types de données non spécifiques tels que les entiers ou les chaînes. L'exemple ci-dessus illustre la façon dont cela peut provoquer des bugs. Heureusement, le //base de Chromium permet d'introduire des types distincts et explicites:

#include "base/types/strong_alias.h"

// The first template argument of StrongAlias is a "tag" type.
// The "tag" type is used to distinguish between different
// StrongAlias types.
using CommandData = base::StrongAlias<class CommandDataTag, int>;
using CommandBufferId = base::StrongAlias<class CommandBufferIdTag, int>;

Token CreateToken(CommandData command_data, CommandBufferId buffer_id);

Des types distincts améliorent la lisibilité. De plus, StrongAlias détecte les erreurs de type au moment de la compilation:

test.cc:456:16: error: no matching function for call to 'CreateToken'
  auto token = CreateToken(GetCommandBufferId(), GetCommandData());
               ^~~~~~~~~~~
test.cc:123:7: note: candidate function not viable: no known conversion from
'StrongAlias<class CommandBufferIdTag, [...]>' to
'StrongAlias<class CommandDataTag, [...]>' for 1st argument
Token CreateToken(CommandData command_data, CommandBufferId buffer_id);
      ^

Le compilateur constate que les types sont incompatibles, car ils ont un type de "balise" différent. StrongAlias accepte n'importe quel type de balise. L'exemple montre que le type "tag" n'a même besoin d'aucune définition de type. Une déclaration avant sur place d'une classe inexistante convient parfaitement.

À l'avenir, au lieu d'utiliser un type non spécifique (par exemple, une valeur booléenne, un entier ou une chaîne), envisagez les alternatives suivantes:

  • Utilisez base::IdType32<TagType> au lieu de int32_t comme identifiant.
  • Utilisez base::TokenType<TagType> au lieu d'un base::UnguessableToken non spécifique.
  • Utilisez une classe d'énumération au lieu d'une valeur booléenne (par exemple, kForReload, kNotForReload au lieu de true, false).
  • Remplacez les autres types non spécifiques par base::StrongAlias<TagType, SomeWrappedType>.