The Chromium Chronicle #24: StrongAlias, IdType, and TokenType

Episode 24: by Łukasz Anforowicz in Bellevue, WA (August, 2021)
Previous episodes

Can you spot the bug in the code below? Would you see the bug in a code review, when looking only at the callsite?

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

The same type may sometimes represent values from incompatible domains. This usually happens for non-specific data types like integers or strings. The example above illustrates how this can cause bugs. Fortunately, Chromium's //base makes it easy to introduce explicit, distinct types:

#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 types improve readability. Additionally, StrongAlias catches type mix-ups at compile time:

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

The compiler sees that the types are incompatible, because they have a different "tag" type. StrongAlias accepts any type as the "tag" type. The example shows that the "tag" type doesn't even need a type definition anywhere—an in-place forward declaration of a non-existent class works fine.

In the future, instead of a non-specific type (for example, a bool, an int, a string), consider these alternatives:

  • Use base::IdType32<TagType> instead of using int32_t as an identifier.
  • Use base::TokenType<TagType> instead of a non-specific base::UnguessableToken.
  • Use an enum class instead of a bool (for example, kForReload, kNotForReload instead of true, false).
  • Replace other non-specific types with base::StrongAlias<TagType, SomeWrappedType>.