• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <ftl/details/mixins.h>
20 
21 namespace android::ftl {
22 
23 // CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common
24 // uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must
25 // be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic,
26 // etc.) are enabled through inheritance:
27 //
28 //   struct Id : ftl::Constructible<Id, std::int32_t>, ftl::Equatable<Id> {
29 //     using Constructible::Constructible;
30 //   };
31 //
32 //   static_assert(!std::is_default_constructible_v<Id>);
33 //
34 // Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is
35 // zero-initialized unless specified:
36 //
37 //   struct Color : ftl::DefaultConstructible<Color, std::uint8_t>,
38 //                  ftl::Equatable<Color>,
39 //                  ftl::Orderable<Color> {
40 //     using DefaultConstructible::DefaultConstructible;
41 //   };
42 //
43 //   static_assert(Color() == Color(0u));
44 //   static_assert(ftl::to_underlying(Color(-1)) == 255u);
45 //   static_assert(Color(1u) < Color(2u));
46 //
47 //   struct Sequence : ftl::DefaultConstructible<Sequence, std::int8_t, -1>,
48 //                     ftl::Equatable<Sequence>,
49 //                     ftl::Orderable<Sequence>,
50 //                     ftl::Incrementable<Sequence> {
51 //     using DefaultConstructible::DefaultConstructible;
52 //   };
53 //
54 //   static_assert(Sequence() == Sequence(-1));
55 //
56 // The underlying type need not be a fundamental type:
57 //
58 //   struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>,
59 //                    ftl::Equatable<Timeout>,
60 //                    ftl::Addable<Timeout> {
61 //     using DefaultConstructible::DefaultConstructible;
62 //   };
63 //
64 //   using namespace std::chrono_literals;
65 //   static_assert(Timeout() + Timeout(5s) == Timeout(15s));
66 //
67 template <typename Self, typename T>
68 struct Constructible {
ConstructibleConstructible69   explicit constexpr Constructible(T value) : value_(value) {}
70 
71   explicit constexpr operator const T&() const { return value_; }
72 
73  private:
74   template <typename, template <typename> class>
75   friend class details::Mixin;
76 
77   T value_;
78 };
79 
80 template <typename Self, typename T, auto kDefault = T{}>
81 struct DefaultConstructible : Constructible<Self, T> {
82   using Constructible<Self, T>::Constructible;
DefaultConstructibleDefaultConstructible83   constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {}
84 };
85 
86 // Shorthand for casting a type-safe wrapper to its underlying value.
87 template <typename Self, typename T>
to_underlying(const Constructible<Self,T> & c)88 constexpr const T& to_underlying(const Constructible<Self, T>& c) {
89   return static_cast<const T&>(c);
90 }
91 
92 // Comparison operators for equality.
93 template <typename Self>
94 struct Equatable : details::Mixin<Self, Equatable> {
95   constexpr bool operator==(const Self& other) const {
96     return to_underlying(this->self()) == to_underlying(other);
97   }
98 
99   constexpr bool operator!=(const Self& other) const { return !(*this == other); }
100 };
101 
102 // Comparison operators for ordering.
103 template <typename Self>
104 struct Orderable : details::Mixin<Self, Orderable> {
105   constexpr bool operator<(const Self& other) const {
106     return to_underlying(this->self()) < to_underlying(other);
107   }
108 
109   constexpr bool operator>(const Self& other) const { return other < this->self(); }
110   constexpr bool operator>=(const Self& other) const { return !(*this < other); }
111   constexpr bool operator<=(const Self& other) const { return !(*this > other); }
112 };
113 
114 // Pre-increment and post-increment operators.
115 template <typename Self>
116 struct Incrementable : details::Mixin<Self, Incrementable> {
117   constexpr Self& operator++() {
118     ++this->mut();
119     return this->self();
120   }
121 
122   constexpr Self operator++(int) {
123     const Self tmp = this->self();
124     operator++();
125     return tmp;
126   }
127 };
128 
129 // Additive operators, including incrementing.
130 template <typename Self>
131 struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> {
132   constexpr Self& operator+=(const Self& other) {
133     this->mut() += to_underlying(other);
134     return this->self();
135   }
136 
137   constexpr Self operator+(const Self& other) const {
138     Self tmp = this->self();
139     return tmp += other;
140   }
141 
142  private:
143   using Base = details::Mixin<Self, Addable>;
144   using Base::mut;
145   using Base::self;
146 };
147 
148 }  // namespace android::ftl
149