blob: 98463db22f548d2c24acdb90f1a8fadf0437bc7d [file] [log] [blame]
// 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_