1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
base / types / id_type.h [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TYPES_ID_TYPE_H_
#define BASE_TYPES_ID_TYPE_H_
#include <cstdint>
#include <functional>
#include <type_traits>
#include "base/ranges/algorithm.h"
#include "base/types/strong_alias.h"
namespace base {
// A specialization of StrongAlias for integer-based identifiers.
//
// IdType32<>, IdType64<>, etc. wrap an integer id in a custom, type-safe type.
//
// IdType32<Foo> is an alternative to int, for a class Foo with methods like:
//
// int GetId() { return id_; };
// static Foo* FromId(int id) { return g_all_foos_by_id[id]; }
//
// Such methods are a standard means of safely referring to objects across
// thread and process boundaries. But if a nearby class Bar also represents
// its IDs as a bare int, horrific mixups are possible -- one example, of many,
// is http://crrev.com/365437. IdType<> offers compile-time protection against
// such mishaps, since IdType32<Foo> is incompatible with IdType32<Bar>, even
// though both just compile down to an int32_t.
//
// Templates in this file:
// IdType32<T> / IdTypeU32<T>: Signed / unsigned 32-bit IDs
// IdType64<T> / IdTypeU64<T>: Signed / unsigned 64-bit IDs
// IdType<>: For when you need a different underlying type or
// default/null values other than zero.
//
// IdType32<Foo> behaves just like an int32_t in the following aspects:
// - it can be used as a key in std::map;
// - it can be used as a key in std::unordered_map
// - it can be used as an argument to DCHECK_EQ or streamed to LOG(ERROR);
// - it has the same memory footprint and runtime overhead as int32_t;
// - it can be copied by memcpy.
// - it can be used in IPC messages.
//
// IdType32<Foo> has the following differences from a bare int32_t:
// - it forces coercions to go through the explicit constructor and value()
// getter;
// - it restricts the set of available operations (e.g. no multiplication);
// - it default-constructs to a null value and allows checking against the null
// value via is_null method.
// - optionally it may have additional values that are all considered null.
template <typename TypeMarker,
typename WrappedType,
WrappedType kInvalidValue,
WrappedType kFirstGeneratedId = kInvalidValue + 1,
WrappedType... kExtraInvalidValues>
class IdType : public StrongAlias<TypeMarker, WrappedType> {
public:
static constexpr WrappedType kAllInvalidValues[] = {kInvalidValue,
kExtraInvalidValues...};
static_assert(std::is_unsigned_v<WrappedType> ||
base::ranges::all_of(kAllInvalidValues,
[](WrappedType v) { return v <= 0; }),
"If signed, invalid values should be negative or equal to zero "
"to avoid overflow issues.");
static_assert(base::ranges::all_of(kAllInvalidValues,
[](WrappedType v) {
return kFirstGeneratedId != v;
}),
"The first generated ID cannot be invalid.");
static_assert(std::is_unsigned_v<WrappedType> ||
base::ranges::all_of(kAllInvalidValues,
[](WrappedType v) {
return kFirstGeneratedId > v;
}),
"If signed, the first generated ID must be greater than all "
"invalid values so that the monotonically increasing "
"GenerateNextId method will never return an invalid value.");
using StrongAlias<TypeMarker, WrappedType>::StrongAlias;
// This class can be used to generate unique IdTypes. It keeps an internal
// counter that is continually increased by one every time an ID is generated.
class Generator {
public:
Generator() = default;
// Generates the next unique ID.
IdType GenerateNextId() { return FromUnsafeValue(next_id_++); }
// Non-copyable.
Generator(const Generator&) = delete;
Generator& operator=(const Generator&) = delete;
private:
WrappedType next_id_ = kFirstGeneratedId;
};
// Default-construct in the null state.
constexpr IdType()
: StrongAlias<TypeMarker, WrappedType>::StrongAlias(kInvalidValue) {}
constexpr bool is_null() const {
return base::ranges::any_of(kAllInvalidValues, [this](WrappedType value) {
return this->value() == value;
});
}
constexpr explicit operator bool() const { return !is_null(); }
// TODO(mpawlowski) Replace these with constructor/value() getter. The
// conversions are safe as long as they're explicit (which is taken care of by
// StrongAlias).
constexpr static IdType FromUnsafeValue(WrappedType value) {
return IdType(value);
}
constexpr WrappedType GetUnsafeValue() const { return this->value(); }
};
// Type aliases for convenience:
template <typename TypeMarker>
using IdType32 = IdType<TypeMarker, std::int32_t, 0>;
template <typename TypeMarker>
using IdTypeU32 = IdType<TypeMarker, std::uint32_t, 0>;
template <typename TypeMarker>
using IdType64 = IdType<TypeMarker, std::int64_t, 0>;
template <typename TypeMarker>
using IdTypeU64 = IdType<TypeMarker, std::uint64_t, 0>;
} // namespace base
template <typename TypeMarker,
typename WrappedType,
WrappedType kInvalidValue,
WrappedType kFirstGeneratedId,
WrappedType... kExtraInvalidValues>
struct std::hash<base::IdType<TypeMarker,
WrappedType,
kInvalidValue,
kFirstGeneratedId,
kExtraInvalidValues...>> {
size_t operator()(const base::IdType<TypeMarker,
WrappedType,
kInvalidValue,
kFirstGeneratedId,
kExtraInvalidValues...>& id) const {
return std::hash<WrappedType>()(id.value());
}
};
#endif // BASE_TYPES_ID_TYPE_H_