From dfeded73f45229558693e227bc1c9f52102ea0af Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Tue, 15 Nov 2022 16:53:53 -0500 Subject: FTL: Add mixins for type-safe wrappers For now, allow mixing in construction, default construction, equality, ordering, incrementing, and addition. Bug: 185536303 Test: ftl_test Change-Id: I9017e124656ba0e7f13f378814c5b1c19a36655b --- include/ftl/mixins.h | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 include/ftl/mixins.h (limited to 'include/ftl/mixins.h') diff --git a/include/ftl/mixins.h b/include/ftl/mixins.h new file mode 100644 index 0000000000..0e1d2004a3 --- /dev/null +++ b/include/ftl/mixins.h @@ -0,0 +1,148 @@ +/* + * 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 -- cgit v1.2.3-59-g8ed1b