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
  157
  158
  159

base / types / strong_alias.h [blame]

// Copyright 2019 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_STRONG_ALIAS_H_
#define BASE_TYPES_STRONG_ALIAS_H_

#include <compare>
#include <functional>
#include <ostream>
#include <type_traits>
#include <utility>

#include "base/trace_event/base_tracing_forward.h"
#include "base/types/supports_ostream_operator.h"

namespace base {

// A type-safe alternative for a typedef or a 'using' directive.
//
// C++ currently does not support type-safe typedefs, despite multiple proposals
// (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The
// next best thing is to try and emulate them in library code.
//
// The motivation is to disallow several classes of errors:
//
// using Orange = int;
// using Apple = int;
// Apple apple(2);
// Orange orange = apple;  // Orange should not be able to become an Apple.
// Orange x = orange + apple;  // Shouldn't add Oranges and Apples.
// if (orange > apple);  // Shouldn't compare Apples to Oranges.
// void foo(Orange);
// void foo(Apple);  // Redefinition.
// etc.
//
// StrongAlias may instead be used as follows:
//
// using Orange = StrongAlias<class OrangeTag, int>;
// using Apple = StrongAlias<class AppleTag, int>;
// using Banana = StrongAlias<class BananaTag, std::string>;
// Apple apple(2);
// Banana banana("Hello");
// Orange orange = apple;  // Does not compile.
// Orange other_orange = orange;  // Compiles, types match.
// Orange x = orange + apple;  // Does not compile.
// Orange y = Orange(orange.value() + apple.value());  // Compiles.
// Orange z = Orange(banana->size() + *other_orange);  // Compiles.
// if (orange > apple);  // Does not compile.
// if (orange > other_orange);  // Compiles.
// void foo(Orange);
// void foo(Apple);  // Compiles into separate overload.
//
// StrongAlias is a zero-cost abstraction, it's compiled away.
//
// TagType is an empty tag class (also called "phantom type") that only serves
// the type system to differentiate between different instantiations of the
// template.
// UnderlyingType may be almost any value type. Note that some methods of the
// StrongAlias may be unavailable (ie. produce elaborate compilation errors when
// used) if UnderlyingType doesn't support them.
//
// StrongAlias only directly exposes comparison operators (for convenient use in
// ordered containers). It's impossible, without reflection, to expose all
// methods of the UnderlyingType in StrongAlias's interface. It's also
// potentially unwanted (ex. you don't want to be able to add two StrongAliases
// that represent socket handles). A getter and dereference operators are
// provided in case you need to access the UnderlyingType.
//
// See also
// - //styleguide/c++/blink-c++.md which provides recommendation and examples of
//   using StrongAlias<Tag, bool> instead of a bare bool.
// - IdType<...> which provides helpers for specializing StrongAlias to be
//   used as an id.
// - TokenType<...> which provides helpers for specializing StrongAlias to be
//   used as a wrapper of base::UnguessableToken.
template <typename TagType, typename UnderlyingType>
class StrongAlias {
 public:
  using underlying_type = UnderlyingType;

  StrongAlias() = default;
  constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
  constexpr explicit StrongAlias(UnderlyingType&& v) noexcept
      : value_(std::move(v)) {}

  constexpr UnderlyingType* operator->() { return &value_; }
  constexpr const UnderlyingType* operator->() const { return &value_; }

  constexpr UnderlyingType& operator*() & { return value_; }
  constexpr const UnderlyingType& operator*() const& { return value_; }
  constexpr UnderlyingType&& operator*() && { return std::move(value_); }
  constexpr const UnderlyingType&& operator*() const&& {
    return std::move(value_);
  }

  constexpr UnderlyingType& value() & { return value_; }
  constexpr const UnderlyingType& value() const& { return value_; }
  constexpr UnderlyingType&& value() && { return std::move(value_); }
  constexpr const UnderlyingType&& value() const&& { return std::move(value_); }

  constexpr explicit operator const UnderlyingType&() const& { return value_; }

  // Comparison operators that default to the behavior of `UnderlyingType`.
  // Note that if you wish to compare `StrongAlias<UnderlyingType>`, e.g.,
  // by using `operator<` in a `std::set`, then `UnderlyingType` must
  // implement `operator<=>`. If you cannot modify `UnderlyingType` (e.g.,
  // because it is from an external library), then a work-around is to create a
  // thin wrapper `W` around it, define `operator<=>` for the wrapper and create
  // a `StrongAlias<W>`.
  friend auto operator<=>(const StrongAlias& lhs,
                          const StrongAlias& rhs) = default;
  friend bool operator==(const StrongAlias& lhs,
                         const StrongAlias& rhs) = default;

  // Hasher to use in std::unordered_map, std::unordered_set, etc.
  //
  // Example usage:
  //     using MyType = base::StrongAlias<...>;
  //     using MySet = std::unordered_set<MyType, typename MyType::Hasher>;
  struct Hasher {
    using argument_type = StrongAlias;
    using result_type = std::size_t;
    result_type operator()(const argument_type& id) const {
      return std::hash<UnderlyingType>()(id.value());
    }
  };

  // If UnderlyingType can be serialised into trace, its alias is also
  // serialisable.
  template <class U = UnderlyingType>
  typename perfetto::check_traced_value_support<U>::type WriteIntoTrace(
      perfetto::TracedValue&& context) const {
    perfetto::WriteIntoTracedValue(std::move(context), value_);
  }

 protected:
  UnderlyingType value_;
};

// Stream operator for convenience, streams the UnderlyingType.
template <typename TagType, typename UnderlyingType>
  requires(internal::SupportsOstreamOperator<UnderlyingType>)
std::ostream& operator<<(std::ostream& stream,
                         const StrongAlias<TagType, UnderlyingType>& alias) {
  return stream << alias.value();
}

}  // namespace base

template <typename TagType, typename UnderlyingType>
struct std::hash<base::StrongAlias<TagType, UnderlyingType>> {
  size_t operator()(
      const base::StrongAlias<TagType, UnderlyingType>& id) const {
    return std::hash<UnderlyingType>()(id.value());
  }
};

#endif  // BASE_TYPES_STRONG_ALIAS_H_