Chromium Chronicle Nr. 24: StrongAlias, IdType und TokenType

Folge 24:von Łukasz Anforowicz in Bellevue, Washington (August 2021)
Vorherige Folgen

Erkennst du den Fehler im Code unten? Würdest du den Fehler in einer Codeüberprüfung sehen, wenn du nur die Anrufwebsite betrachtest?

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

Derselbe Typ kann manchmal für Werte aus inkompatiblen Domains stehen. Dies geschieht normalerweise bei unspezifischen Datentypen wie Ganzzahlen oder Strings. Das Beispiel oben zeigt, wie dies zu Fehlern führen kann. Mit //base von Chromium ist die Einführung von expliziten, unterschiedlichen Typen ganz einfach:

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

Separate Typen verbessern die Lesbarkeit. Außerdem fängt StrongAlias Typverwechslungen zur Kompilierungszeit ab:

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

Der Compiler erkennt, dass die Typen nicht kompatibel sind, da sie einen anderen "Tag"-Typ haben. StrongAlias akzeptiert jeden beliebigen Typ als Tag-Typ. Das Beispiel zeigt, dass für den "Tag"-Typ nicht einmal eine Typdefinition erforderlich ist. Die In-Place-Forward-Deklaration einer nicht vorhandenen Klasse funktioniert problemlos.

Ziehen Sie künftig anstelle eines unspezifischen Typs (z. B. boolescher Wert, Ganzzahl, String) folgende Alternativen in Betracht:

  • Verwenden Sie base::IdType32<TagType> anstelle von int32_t als Kennung.
  • Verwende base::TokenType<TagType> anstelle einer unspezifischen base::UnguessableToken.
  • Verwenden Sie eine Enum-Klasse anstelle eines booleschen Werts (z. B. kForReload, kNotForReload anstelle von true, false).
  • Ersetzen Sie andere unspezifische Typen durch base::StrongAlias<TagType, SomeWrappedType>.