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_