strong_alias.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Copyright 2019 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. #ifndef BASE_TYPES_STRONG_ALIAS_H_
  5. #define BASE_TYPES_STRONG_ALIAS_H_
  6. #include <ostream>
  7. #include <type_traits>
  8. #include <utility>
  9. #include "base/template_util.h"
  10. #include "base/trace_event/base_tracing_forward.h"
  11. namespace base {
  12. // A type-safe alternative for a typedef or a 'using' directive.
  13. //
  14. // C++ currently does not support type-safe typedefs, despite multiple proposals
  15. // (ex. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3515.pdf). The
  16. // next best thing is to try and emulate them in library code.
  17. //
  18. // The motivation is to disallow several classes of errors:
  19. //
  20. // using Orange = int;
  21. // using Apple = int;
  22. // Apple apple(2);
  23. // Orange orange = apple; // Orange should not be able to become an Apple.
  24. // Orange x = orange + apple; // Shouldn't add Oranges and Apples.
  25. // if (orange > apple); // Shouldn't compare Apples to Oranges.
  26. // void foo(Orange);
  27. // void foo(Apple); // Redefinition.
  28. // etc.
  29. //
  30. // StrongAlias may instead be used as follows:
  31. //
  32. // using Orange = StrongAlias<class OrangeTag, int>;
  33. // using Apple = StrongAlias<class AppleTag, int>;
  34. // using Banana = StrongAlias<class BananaTag, std::string>;
  35. // Apple apple(2);
  36. // Banana banana("Hello");
  37. // Orange orange = apple; // Does not compile.
  38. // Orange other_orange = orange; // Compiles, types match.
  39. // Orange x = orange + apple; // Does not compile.
  40. // Orange y = Orange(orange.value() + apple.value()); // Compiles.
  41. // Orange z = Orange(banana->size() + *other_orange); // Compiles.
  42. // if (orange > apple); // Does not compile.
  43. // if (orange > other_orange); // Compiles.
  44. // void foo(Orange);
  45. // void foo(Apple); // Compiles into separate overload.
  46. //
  47. // StrongAlias is a zero-cost abstraction, it's compiled away.
  48. //
  49. // TagType is an empty tag class (also called "phantom type") that only serves
  50. // the type system to differentiate between different instantiations of the
  51. // template.
  52. // UnderlyingType may be almost any value type. Note that some methods of the
  53. // StrongAlias may be unavailable (ie. produce elaborate compilation errors when
  54. // used) if UnderlyingType doesn't support them.
  55. //
  56. // StrongAlias only directly exposes comparison operators (for convenient use in
  57. // ordered containers) and a Hasher struct (for unordered_map/set). It's
  58. // impossible, without reflection, to expose all methods of the UnderlyingType
  59. // in StrongAlias's interface. It's also potentially unwanted (ex. you don't
  60. // want to be able to add two StrongAliases that represent socket handles).
  61. // A getter and dereference operators are provided in case you need to access
  62. // the UnderlyingType.
  63. //
  64. // See also
  65. // - //styleguide/c++/blink-c++.md which provides recommendation and examples of
  66. // using StrongAlias<Tag, bool> instead of a bare bool.
  67. // - IdType<...> which provides helpers for specializing StrongAlias to be
  68. // used as an id.
  69. // - TokenType<...> which provides helpers for specializing StrongAlias to be
  70. // used as a wrapper of base::UnguessableToken.
  71. template <typename TagType, typename UnderlyingType>
  72. class StrongAlias {
  73. public:
  74. constexpr StrongAlias() = default;
  75. constexpr explicit StrongAlias(const UnderlyingType& v) : value_(v) {}
  76. constexpr explicit StrongAlias(UnderlyingType&& v) noexcept
  77. : value_(std::move(v)) {}
  78. constexpr UnderlyingType* operator->() { return &value_; }
  79. constexpr const UnderlyingType* operator->() const { return &value_; }
  80. constexpr UnderlyingType& operator*() & { return value_; }
  81. constexpr const UnderlyingType& operator*() const& { return value_; }
  82. constexpr UnderlyingType&& operator*() && { return std::move(value_); }
  83. constexpr const UnderlyingType&& operator*() const&& {
  84. return std::move(value_);
  85. }
  86. constexpr UnderlyingType& value() & { return value_; }
  87. constexpr const UnderlyingType& value() const& { return value_; }
  88. constexpr UnderlyingType&& value() && { return std::move(value_); }
  89. constexpr const UnderlyingType&& value() const&& { return std::move(value_); }
  90. constexpr explicit operator const UnderlyingType&() const& { return value_; }
  91. constexpr bool operator==(const StrongAlias& other) const {
  92. return value_ == other.value_;
  93. }
  94. constexpr bool operator!=(const StrongAlias& other) const {
  95. return value_ != other.value_;
  96. }
  97. constexpr bool operator<(const StrongAlias& other) const {
  98. return value_ < other.value_;
  99. }
  100. constexpr bool operator<=(const StrongAlias& other) const {
  101. return value_ <= other.value_;
  102. }
  103. constexpr bool operator>(const StrongAlias& other) const {
  104. return value_ > other.value_;
  105. }
  106. constexpr bool operator>=(const StrongAlias& other) const {
  107. return value_ >= other.value_;
  108. }
  109. // Hasher to use in std::unordered_map, std::unordered_set, etc.
  110. //
  111. // Example usage:
  112. // using MyType = base::StrongAlias<...>;
  113. // using MySet = std::unordered_set<MyType, typename MyType::Hasher>;
  114. //
  115. // https://google.github.io/styleguide/cppguide.html#std_hash asks to avoid
  116. // defining specializations of `std::hash` - this is why the hasher needs to
  117. // be explicitly specified and why the following code will *not* work:
  118. // using MyType = base::StrongAlias<...>;
  119. // using MySet = std::unordered_set<MyType>; // This won't work.
  120. struct Hasher {
  121. using argument_type = StrongAlias;
  122. using result_type = std::size_t;
  123. result_type operator()(const argument_type& id) const {
  124. return std::hash<UnderlyingType>()(id.value());
  125. }
  126. };
  127. // If UnderlyingType can be serialised into trace, its alias is also
  128. // serialisable.
  129. template <class U = UnderlyingType>
  130. typename perfetto::check_traced_value_support<U>::type WriteIntoTrace(
  131. perfetto::TracedValue&& context) const {
  132. perfetto::WriteIntoTracedValue(std::move(context), value_);
  133. }
  134. protected:
  135. UnderlyingType value_;
  136. };
  137. // Stream operator for convenience, streams the UnderlyingType.
  138. template <typename TagType,
  139. typename UnderlyingType,
  140. typename = std::enable_if_t<
  141. base::internal::SupportsOstreamOperator<UnderlyingType>::value>>
  142. std::ostream& operator<<(std::ostream& stream,
  143. const StrongAlias<TagType, UnderlyingType>& alias) {
  144. return stream << alias.value();
  145. }
  146. } // namespace base
  147. #endif // BASE_TYPES_STRONG_ALIAS_H_