blob: 0e1d2004a3ab788d3208f48fbd679625397199a8 [file] [log] [blame]
Dominik Laskowskidfeded72022-11-15 16:53:53 -05001/*
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
21namespace 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//
67template <typename Self, typename T>
68struct Constructible {
69 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
80template <typename Self, typename T, auto kDefault = T{}>
81struct DefaultConstructible : Constructible<Self, T> {
82 using Constructible<Self, T>::Constructible;
83 constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {}
84};
85
86// Shorthand for casting a type-safe wrapper to its underlying value.
87template <typename Self, typename T>
88constexpr const T& to_underlying(const Constructible<Self, T>& c) {
89 return static_cast<const T&>(c);
90}
91
92// Comparison operators for equality.
93template <typename Self>
94struct 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.
103template <typename Self>
104struct 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.
115template <typename Self>
116struct 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.
130template <typename Self>
131struct 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