/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include namespace android::ftl { // CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common // uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must // be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic, // etc.) are enabled through inheritance: // // struct Id : ftl::Constructible, ftl::Equatable { // using Constructible::Constructible; // }; // // static_assert(!std::is_default_constructible_v); // // Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is // zero-initialized unless specified: // // struct Color : ftl::DefaultConstructible, // ftl::Equatable, // ftl::Orderable { // using DefaultConstructible::DefaultConstructible; // }; // // static_assert(Color() == Color(0u)); // static_assert(ftl::to_underlying(Color(-1)) == 255u); // static_assert(Color(1u) < Color(2u)); // // struct Sequence : ftl::DefaultConstructible, // ftl::Equatable, // ftl::Orderable, // ftl::Incrementable { // using DefaultConstructible::DefaultConstructible; // }; // // static_assert(Sequence() == Sequence(-1)); // // The underlying type need not be a fundamental type: // // struct Timeout : ftl::DefaultConstructible, // ftl::Equatable, // ftl::Addable { // using DefaultConstructible::DefaultConstructible; // }; // // using namespace std::chrono_literals; // static_assert(Timeout() + Timeout(5s) == Timeout(15s)); // template struct Constructible { explicit constexpr Constructible(T value) : value_(value) {} explicit constexpr operator const T&() const { return value_; } private: template class> friend class details::Mixin; T value_; }; template struct DefaultConstructible : Constructible { using Constructible::Constructible; constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {} }; // Shorthand for casting a type-safe wrapper to its underlying value. template constexpr const T& to_underlying(const Constructible& c) { return static_cast(c); } // Comparison operators for equality. template struct Equatable : details::Mixin { constexpr bool operator==(const Self& other) const { return to_underlying(this->self()) == to_underlying(other); } constexpr bool operator!=(const Self& other) const { return !(*this == other); } }; // Comparison operators for ordering. template struct Orderable : details::Mixin { constexpr bool operator<(const Self& other) const { return to_underlying(this->self()) < to_underlying(other); } constexpr bool operator>(const Self& other) const { return other < this->self(); } constexpr bool operator>=(const Self& other) const { return !(*this < other); } constexpr bool operator<=(const Self& other) const { return !(*this > other); } }; // Pre-increment and post-increment operators. template struct Incrementable : details::Mixin { constexpr Self& operator++() { ++this->mut(); return this->self(); } constexpr Self operator++(int) { const Self tmp = this->self(); operator++(); return tmp; } }; // Additive operators, including incrementing. template struct Addable : details::Mixin, Incrementable { constexpr Self& operator+=(const Self& other) { this->mut() += to_underlying(other); return this->self(); } constexpr Self operator+(const Self& other) const { Self tmp = this->self(); return tmp += other; } private: using Base = details::Mixin; using Base::mut; using Base::self; }; } // namespace android::ftl