OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #ifndef CONTENT_COMMON_ID_TYPE_H_ | |
6 #define CONTENT_COMMON_ID_TYPE_H_ | |
7 | |
8 #include <stdint.h> | |
9 #include <ostream> | |
10 #include <type_traits> | |
11 | |
12 #include "base/containers/hash_tables.h" | |
13 | |
14 // Problem: | |
ncarter (slow)
2016/01/07 20:14:32
For templates like this, the definitions that user
Łukasz Anforowicz
2016/01/07 21:43:01
Good point. Done. OTOH I tweaked your suggestion
| |
15 // | |
16 // void f(int foo_id, int bar_id); | |
17 // ... | |
18 // // Arguments passed in a wrong order: | |
19 // f(bar_id, foo_id); // Compiles - sigh... :-( | |
20 // | |
21 // Solution: | |
22 // | |
23 // using FooId = content::IdType32<Foo>; | |
24 // using BarId = content::IdType32<Bar>; | |
25 // void f(FooId foo_id, BarId bar_id); | |
26 // ... | |
27 // // Arguments passed in a wrong order: | |
28 // f(bar_id, foo_id); // Doesn't compile anymore - yay! :-) | |
29 // | |
30 // Typically FooId would be declared like this: | |
31 // | |
32 // foo_id.h: | |
33 // | |
34 // #include <stdint.h> | |
35 // #include "content/common/id_type.h" | |
36 // | |
37 // namespace foo_namespace { | |
38 // | |
39 // class Foo; | |
40 // using FooId = content::IdType<Foo, uint64_t, 0>; | |
41 // | |
42 // // A type alias provided by id_type.h can also be used: | |
43 // // using FooId = content::IdType64<Foo>; | |
44 // | |
45 // } // namespace foo_namespace | |
46 // | |
47 // For most practical purposes, FooId declared like above behaves just like | |
48 // an uint64_t, except that: | |
49 // | |
50 // 1. FooId only supports a subset of uint64_t operations: | |
51 // - ==, !=, <, hashing | |
52 // - stream output | |
53 // - copy construction and assignment | |
ncarter (slow)
2016/01/07 20:14:32
Would it be better to talk about what the supporte
Łukasz Anforowicz
2016/01/07 21:43:01
Done.
I've added a paragraph following your sugge
| |
54 // | |
55 // 2. FooId does not implicitly coerce to/from other uint64_t values and | |
56 // to/from content::IdType<SomeOtherType, ...>. Explicit coercion to/from | |
57 // uint64_t is possible through the following FooId methods: | |
58 // - static FooId FromUnsafeValue(uint64_t) | |
59 // - uint64_t GetUnsafeValue() | |
60 // | |
61 // 3. Default-constructed FooId contains a "null" / "invalid" value specified | |
62 // by the 3rd argument of the IdType<...> template. Testing against this | |
63 // value can be done by the following FooId methods: | |
64 // - bool is_valid() | |
65 // - bool is_null() | |
66 // | |
67 // 4. The presence of a custom default constructor means that FooId is not a | |
68 // "trivial" class and therefore is not a POD type (unlike an uint64_t). | |
69 // At the same time FooId has almost all of the properties of a POD type: | |
70 // - is "trivially copyable" (i.e. is memcpy-able), | |
71 // - has "standard layout" (i.e. interops with things expecting C layout). | |
72 // See http://stackoverflow.com/a/7189821 for more info about these | |
73 // concepts. | |
74 | |
75 namespace content { | |
76 | |
77 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue> | |
78 class IdType { | |
79 public: | |
80 IdType() : value_(kInvalidValue) {} | |
81 bool is_valid() const { return value_ != kInvalidValue; } | |
82 bool is_null() const { return value_ == kInvalidValue; } | |
83 | |
84 static IdType FromUnsafeValue(WrappedType value) { return IdType(value); } | |
85 WrappedType GetUnsafeValue() const { return value_; } | |
86 | |
87 IdType(const IdType& other) = default; | |
88 IdType& operator=(const IdType& other) = default; | |
89 | |
90 bool operator==(const IdType& other) const { return value_ == other.value_; } | |
91 bool operator!=(const IdType& other) const { return value_ != other.value_; } | |
92 bool operator<(const IdType& other) const { return value_ < other.value_; } | |
93 | |
94 protected: | |
95 explicit IdType(WrappedType val) : value_(val) {} | |
96 | |
97 private: | |
98 // In theory WrappedType could be any type that supports ==, <, <<, std::hash, | |
99 // etc., but to make things simpler (both for users and for maintainers) we | |
100 // explicitly restrict the design space to integers. This means the users | |
101 // can safely assume that IdType is relatively small and cheap to copy | |
102 // and the maintainers don't have to worry about WrappedType being a complex | |
103 // type (i.e. std::string or std::pair or a move-only type). | |
104 using IntegralWrappedType = | |
105 typename std::enable_if<std::is_integral<WrappedType>::value, | |
106 WrappedType>::type; | |
107 IntegralWrappedType value_; | |
108 }; | |
109 | |
110 // Type aliases for convenience: | |
111 template <typename TypeMarker> | |
112 using IdType32 = IdType<TypeMarker, uint32_t, 0>; | |
113 template <typename TypeMarker> | |
114 using IdType64 = IdType<TypeMarker, uint64_t, 0>; | |
115 | |
116 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue> | |
117 std::ostream& operator<<( | |
118 std::ostream& stream, | |
119 const IdType<TypeMarker, WrappedType, kInvalidValue>& id) { | |
120 return stream << id.GetUnsafeValue(); | |
121 } | |
122 | |
123 } // namespace content | |
124 | |
125 namespace BASE_HASH_NAMESPACE { | |
126 | |
127 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue> | |
128 struct hash<content::IdType<TypeMarker, WrappedType, kInvalidValue>> { | |
129 using argument_type = content::IdType<TypeMarker, WrappedType, kInvalidValue>; | |
130 using result_type = std::size_t; | |
131 result_type operator()(const argument_type& id) const { | |
132 return BASE_HASH_NAMESPACE::hash<WrappedType>()(id.GetUnsafeValue()); | |
133 } | |
134 }; | |
135 | |
136 } // namespace BASE_HASH_NAMESPACE | |
137 | |
138 // If defined(COMPILER_MSVC) then BASE_HASH_NAMESPACE == std. | |
139 // In this case we need to avoid defininig std::hash<...> second time | |
140 // (it has already been defined above). | |
141 #if !defined(COMPILER_MSVC) | |
142 | |
143 namespace std { | |
144 | |
145 template <typename TypeMarker, typename WrappedType, WrappedType kInvalidValue> | |
146 struct hash<content::IdType<TypeMarker, WrappedType, kInvalidValue>> { | |
147 using argument_type = content::IdType<TypeMarker, WrappedType, kInvalidValue>; | |
148 using result_type = std::size_t; | |
149 result_type operator()(const argument_type& id) const { | |
150 return std::hash<WrappedType>()(id.GetUnsafeValue()); | |
151 } | |
152 }; | |
153 | |
154 } // namespace std; | |
155 | |
156 #endif // !defined(COMPILER_MSVC) | |
157 | |
158 #endif // CONTENT_COMMON_ID_TYPE_H_ | |
OLD | NEW |