Aflevering 24: door Łukasz Anforowicz in Bellevue, WA (augustus 2021)
Vorige afleveringen
Kun jij de bug in onderstaande code ontdekken? Zou u de bug zien in een codebeoordeling als u alleen naar de callsite kijkt?
Token CreateToken(int command_data, int buffer_id);
...
auto token = CreateToken(GetCommandBufferId(), GetCommandData());
Hetzelfde type vertegenwoordigt soms waarden uit incompatibele domeinen. Dit gebeurt meestal voor niet-specifieke gegevenstypen zoals gehele getallen of tekenreeksen. Het bovenstaande voorbeeld illustreert hoe dit bugs kan veroorzaken. Gelukkig maakt Chromium's //base
het gemakkelijk om expliciete, verschillende typen te introduceren:
#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);
Afzonderlijke typen verbeteren de leesbaarheid. Bovendien vangt StrongAlias
typeverwisselingen op tijdens het compileren:
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);
^
De compiler ziet dat de typen incompatibel zijn, omdat ze een ander "tag"-type hebben. StrongAlias
accepteert elk type als het "tag" -type. Het voorbeeld laat zien dat het type "tag" nergens een typedefinitie nodig heeft; een in-place forward declaratie van een niet-bestaande klasse werkt prima.
Overweeg in de toekomst, in plaats van een niet-specifiek type (bijvoorbeeld een bool, een int, een string), deze alternatieven:
- Gebruik
base::IdType32<TagType>
in plaats vanint32_t
als identificatie te gebruiken. - Gebruik
base::TokenType<TagType>
in plaats van een niet-specifiekebase::UnguessableToken
. - Gebruik een enum-klasse in plaats van een bool (bijvoorbeeld
kForReload
,kNotForReload
in plaats vantrue
,false
). - Vervang andere niet-specifieke typen door
base::StrongAlias<TagType, SomeWrappedType>
.