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