Chromium Chronicle #24: StrongAlias、IdType、TokenType

エピソード 24: ProGuard Anforowicz、ワシントン州ベルビュー(2021 年 8 月)
以前のエピソード

以下のコード内でバグを見つけられますか?コールサイトだけを確認した際に、コードレビューにバグは見つかるでしょうか。

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

同じ型が、互換性のないドメインの値を表す場合があります。 これは通常、整数や文字列など、特定のデータ型がない場合に発生します。上記の例は、どのようにバグが発生する可能性があるかを示しています。 幸いなことに、Chromium の //base では、明確に異なるタイプを簡単に導入できます。

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

型を分けると、読みやすくなります。さらに、StrongAlias はコンパイル時に型の取り違えをキャッチします。

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

「タグ」のタイプが異なるため、コンパイラは、その型に互換性がないと判断します。StrongAlias は「タグ」タイプとして任意のタイプを受け入れます。この例は、「tag」型はどこにも型定義を必要としないことを示しています。存在しないクラスのインプレース前方宣言は適切に機能します。

今後、非特定の型(ブール値、整数、文字列など)を使用する代わりに、次の代替手段を検討してください。

  • 識別子として int32_t を使用する代わりに、base::IdType32<TagType> を使用してください。
  • 非具体的な base::UnguessableToken ではなく base::TokenType<TagType> を使用します。
  • ブール値ではなく列挙型クラスを使用します(例: truefalse ではなく kForReloadkNotForReload)。
  • 他の非特定のタイプを base::StrongAlias<TagType, SomeWrappedType> に置き換えます。