| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COBALT_SRC_LIB_UTIL_NAMED_TYPE_H_ |
| #define COBALT_SRC_LIB_UTIL_NAMED_TYPE_H_ |
| |
| #include <type_traits> |
| #include <utility> |
| |
| namespace cobalt::util { |
| |
| template <typename T> |
| using IsNotReference = typename std::enable_if<!std::is_reference<T>::value, void>::type; |
| |
| // NamedType is used to wrap types (such as uint32_t) to allow them to be |
| // disambiguated from other instances of the same type. |
| // |
| // Note: this class is based on the idea discussed in |
| // https://www.fluentcpp.com/2017/05/05/news-strong-types-are-free |
| // |
| // Example: |
| // void do_something(int height, int width); |
| // |
| // int height = 10; |
| // int width = 10; |
| // do_something(width, height); // Wrong order, but it still compiles! |
| // |
| // using Height = NamedType<int, struct HeightTag>; |
| // using Width = NamedType<int, struct WidthTag>; |
| // void do_something(Height height, Width width); |
| // |
| // Height height(10); |
| // Width width(10); |
| // do_something(width, height); // Wrong order, but compile error! |
| // |
| // Note: The 'Tag' is a uique empty struct. If two instances of |
| // NamedType in the same namespace use the same struct name, they will |
| // be considered the same type. This is likely undesirable, so the Tag |
| // struct should be unique. |
| template <typename T, typename Tag> |
| class NamedType { |
| public: |
| // Explicit constructor from base type. (e.g. Height(10)) |
| explicit constexpr NamedType(T value) : value_(std::move(value)) {} |
| |
| // Move constructor. This is only available for instances of NamedType |
| // where the base type T is not a reference. |
| template <typename T_ = T, typename = IsNotReference<T_>> |
| explicit constexpr NamedType(T&& value) : value_(std::move(value)) {} |
| |
| // Access the inner value of NamedType mutably. |
| [[nodiscard]] constexpr T& get() { return value_; } |
| |
| // Access the inner value of NamedType immutably. (remove_reference_t |
| // in the case where the contained type is a reference, this will |
| // return a copy of the contained value). |
| [[nodiscard]] constexpr std::remove_reference_t<T> const& get() const { return value_; } |
| |
| // Addition |
| constexpr NamedType operator+(NamedType const& other) const { |
| return NamedType(this->value_ + other.value_); |
| } |
| NamedType& operator+=(NamedType const& other) { |
| this->value_ += other.value_; |
| return this->value_; |
| } |
| constexpr NamedType operator+() const { return NamedType(+this->value_); } |
| |
| // Subtraction |
| constexpr NamedType operator-(NamedType const& other) const { |
| return NamedType(this->value_ - other.value_); |
| } |
| NamedType& operator-=(NamedType const& other) { |
| this->value_ -= other.value_; |
| return this->value_; |
| } |
| constexpr NamedType operator-() const { return NamedType(-this->value_); } |
| |
| // Printing |
| void print(std::ostream& os) const { os << this->value_; } |
| friend std::ostream& operator<<(std::ostream& os, const NamedType& other) { |
| other.print(os); |
| return os; |
| } |
| |
| private: |
| T value_; |
| }; |
| |
| } // namespace cobalt::util |
| |
| #endif // COBALT_SRC_LIB_UTIL_NAMED_TYPE_H_ |