diff options
author | 2022-11-15 16:53:53 -0500 | |
---|---|---|
committer | 2022-11-22 16:04:41 -0500 | |
commit | dfeded73f45229558693e227bc1c9f52102ea0af (patch) | |
tree | 94b0e2ceb446c3b65fbadd8b5bf848cff8b7d36c /include/ftl | |
parent | 345d32bcbf1fdeae6be3989fc96c7ea9c2ac3957 (diff) |
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
Diffstat (limited to 'include/ftl')
-rw-r--r-- | include/ftl/details/mixins.h | 30 | ||||
-rw-r--r-- | include/ftl/enum.h | 2 | ||||
-rw-r--r-- | include/ftl/mixins.h | 148 |
3 files changed, 179 insertions, 1 deletions
diff --git a/include/ftl/details/mixins.h b/include/ftl/details/mixins.h new file mode 100644 index 0000000000..9ab9e083ae --- /dev/null +++ b/include/ftl/details/mixins.h @@ -0,0 +1,30 @@ +/* + * 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 + +namespace android::ftl::details { + +template <typename Self, template <typename> class> +class Mixin { + protected: + constexpr Self& self() { return *static_cast<Self*>(this); } + constexpr const Self& self() const { return *static_cast<const Self*>(this); } + + constexpr auto& mut() { return self().value_; } +}; + +} // namespace android::ftl::details diff --git a/include/ftl/enum.h b/include/ftl/enum.h index 82af1d6cf8..075d12bd17 100644 --- a/include/ftl/enum.h +++ b/include/ftl/enum.h @@ -92,7 +92,7 @@ inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; // enum class E { A, B, C }; // static_assert(ftl::to_underlying(E::B) == 1); // -template <typename E> +template <typename E, typename = std::enable_if_t<std::is_enum_v<E>>> constexpr auto to_underlying(E v) { return static_cast<std::underlying_type_t<E>>(v); } 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 <ftl/details/mixins.h> + +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<Id, std::int32_t>, ftl::Equatable<Id> { +// using Constructible::Constructible; +// }; +// +// static_assert(!std::is_default_constructible_v<Id>); +// +// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is +// zero-initialized unless specified: +// +// struct Color : ftl::DefaultConstructible<Color, std::uint8_t>, +// ftl::Equatable<Color>, +// ftl::Orderable<Color> { +// 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<Sequence, std::int8_t, -1>, +// ftl::Equatable<Sequence>, +// ftl::Orderable<Sequence>, +// ftl::Incrementable<Sequence> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// static_assert(Sequence() == Sequence(-1)); +// +// The underlying type need not be a fundamental type: +// +// struct Timeout : ftl::DefaultConstructible<Timeout, std::chrono::seconds, 10>, +// ftl::Equatable<Timeout>, +// ftl::Addable<Timeout> { +// using DefaultConstructible::DefaultConstructible; +// }; +// +// using namespace std::chrono_literals; +// static_assert(Timeout() + Timeout(5s) == Timeout(15s)); +// +template <typename Self, typename T> +struct Constructible { + explicit constexpr Constructible(T value) : value_(value) {} + + explicit constexpr operator const T&() const { return value_; } + + private: + template <typename, template <typename> class> + friend class details::Mixin; + + T value_; +}; + +template <typename Self, typename T, auto kDefault = T{}> +struct DefaultConstructible : Constructible<Self, T> { + using Constructible<Self, T>::Constructible; + constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {} +}; + +// Shorthand for casting a type-safe wrapper to its underlying value. +template <typename Self, typename T> +constexpr const T& to_underlying(const Constructible<Self, T>& c) { + return static_cast<const T&>(c); +} + +// Comparison operators for equality. +template <typename Self> +struct Equatable : details::Mixin<Self, Equatable> { + 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 <typename Self> +struct Orderable : details::Mixin<Self, Orderable> { + 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 <typename Self> +struct Incrementable : details::Mixin<Self, Incrementable> { + 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 <typename Self> +struct Addable : details::Mixin<Self, Addable>, Incrementable<Self> { + 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<Self, Addable>; + using Base::mut; + using Base::self; +}; + +} // namespace android::ftl |