エピソード 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>
を使用します。 - ブール値ではなく列挙型クラスを使用します(例:
true
やfalse
ではなくkForReload
、kNotForReload
)。 - 他の非特定のタイプを
base::StrongAlias<TagType, SomeWrappedType>
に置き換えます。