Chronicle Chromium nr 24: StrongAlias, IdType i TokenType

Odcinek 24: Łukasz Anforowicz, Bellevue, Waszyngton (sierpień 2021)
Poprzednie odcinki

Czy widzisz błąd w kodzie poniżej? Czy zobaczysz błąd podczas weryfikacji kodu, podczas analizowania tylko warunków telefonicznych?

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

Ten sam typ może czasami reprezentować wartości z niezgodnych domen. Zwykle dzieje się tak w przypadku nieokreślonych typów danych, takich jak liczby całkowite lub ciągi znaków. Powyższy przykład pokazuje, jak może to powodować błędy. Na szczęście //base w Chromium pozwala łatwo wprowadzić konkretne, wyróżniające się typy:

#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);

Oddzielne typy zwiększają czytelność. Dodatkowo StrongAlias wychwytuje pomyłki typów w czasie kompilowania:

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);
      ^

Kompilator zauważa, że typy są niezgodne, ponieważ mają inny „tag” typu. StrongAlias akceptuje dowolny typ jako „tag” typu. Ten przykład pokazuje, że „tag” nie potrzebuje nawet definicji typu – obowiązująca deklaracja nieistniejącej klasy.

W przyszłości zamiast ogólnego typu (takiego jak wartość logiczna, int czy ciąg znaków), rozważ następujące alternatywne rozwiązania:

  • Użyj base::IdType32<TagType> zamiast int32_t jako identyfikatora.
  • Użyj base::TokenType<TagType> zamiast nieokreślonego typu base::UnguessableToken.
  • Używanie klasy wyliczeniowej zamiast wartości logicznej (na przykład kForReload, kNotForReload zamiast true, false).
  • Zastąp inne, nieokreślone typy właściwościami base::StrongAlias<TagType, SomeWrappedType>.