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