 Chromium Code Reviews
 Chromium Code Reviews Issue 1529363006:
  Introducing SavePackageId and SaveItemId as distinct IdType<...>-based types.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1529363006:
  Introducing SavePackageId and SaveItemId as distinct IdType<...>-based types.  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| 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 |