|
|
|
@ -10,74 +10,199 @@
|
|
|
|
|
#ifndef STRONG_TYPEDEF_TYPE_HPP
|
|
|
|
|
#define STRONG_TYPEDEF_TYPE_HPP
|
|
|
|
|
|
|
|
|
|
/** Non-templated base for #StrongTypedef for use with type trait queries. */
|
|
|
|
|
struct StrongTypedefBase {};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Templated helper to make a type-safe 'typedef' representing a single POD value.
|
|
|
|
|
* A normal 'typedef' is not distinct from its base type and will be treated as
|
|
|
|
|
* identical in many contexts. This class provides a distinct type that can still
|
|
|
|
|
* be assign from and compared to values of its base type.
|
|
|
|
|
*
|
|
|
|
|
* @note This is meant to be used as a base class, not directly.
|
|
|
|
|
* @tparam T Storage type
|
|
|
|
|
* @tparam Tthis Type of the derived class (i.e. the concrete usage of this class).
|
|
|
|
|
*/
|
|
|
|
|
template <class T, class Tthis>
|
|
|
|
|
struct StrongTypedef : StrongTypedefBase {
|
|
|
|
|
using Type = T;
|
|
|
|
|
|
|
|
|
|
T value{}; ///< Backing storage field.
|
|
|
|
|
|
|
|
|
|
debug_inline constexpr StrongTypedef() = default;
|
|
|
|
|
debug_inline constexpr StrongTypedef(const StrongTypedef &o) = default;
|
|
|
|
|
debug_inline constexpr StrongTypedef(StrongTypedef &&o) = default;
|
|
|
|
|
|
|
|
|
|
debug_inline constexpr StrongTypedef(const T &value) : value(value) {}
|
|
|
|
|
|
|
|
|
|
debug_inline constexpr Tthis &operator =(const Tthis &rhs) { this->value = rhs.value; return static_cast<Tthis &>(*this); }
|
|
|
|
|
debug_inline constexpr Tthis &operator =(Tthis &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
|
|
|
|
|
debug_inline constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
|
|
|
|
|
|
|
|
|
|
explicit constexpr operator T() const { return this->value; }
|
|
|
|
|
#include "../3rdparty/fmt/format.h"
|
|
|
|
|
|
|
|
|
|
constexpr bool operator ==(const Tthis &rhs) const { return this->value == rhs.value; }
|
|
|
|
|
constexpr bool operator !=(const Tthis &rhs) const { return this->value != rhs.value; }
|
|
|
|
|
constexpr bool operator ==(const T &rhs) const { return this->value == rhs; }
|
|
|
|
|
constexpr bool operator !=(const T &rhs) const { return this->value != rhs; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Extension of #StrongTypedef with operators for addition and subtraction.
|
|
|
|
|
* @tparam T Storage type
|
|
|
|
|
* @tparam Tthis Type of the derived class (i.e. the concrete usage of this class).
|
|
|
|
|
*/
|
|
|
|
|
template <class T, class Tthis>
|
|
|
|
|
struct StrongIntegralTypedef : StrongTypedef<T, Tthis> {
|
|
|
|
|
using StrongTypedef<T, Tthis>::StrongTypedef;
|
|
|
|
|
|
|
|
|
|
debug_inline constexpr StrongIntegralTypedef() = default;
|
|
|
|
|
debug_inline constexpr StrongIntegralTypedef(const StrongIntegralTypedef &o) = default;
|
|
|
|
|
debug_inline constexpr StrongIntegralTypedef(StrongIntegralTypedef &&o) = default;
|
|
|
|
|
|
|
|
|
|
debug_inline constexpr StrongIntegralTypedef(const T &value) : StrongTypedef<T, Tthis>(value) {}
|
|
|
|
|
|
|
|
|
|
debug_inline constexpr Tthis &operator =(const Tthis &rhs) { this->value = rhs.value; return static_cast<Tthis &>(*this); }
|
|
|
|
|
debug_inline constexpr Tthis &operator =(Tthis &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
|
|
|
|
|
debug_inline constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
|
|
|
|
|
|
|
|
|
|
constexpr Tthis &operator ++() { this->value++; return static_cast<Tthis &>(*this); }
|
|
|
|
|
constexpr Tthis &operator --() { this->value--; return static_cast<Tthis &>(*this); }
|
|
|
|
|
constexpr Tthis operator ++(int) { auto res = static_cast<Tthis &>(*this); this->value++; return res; }
|
|
|
|
|
constexpr Tthis operator --(int) { auto res = static_cast<Tthis &>(*this); this->value--; return res; }
|
|
|
|
|
|
|
|
|
|
constexpr Tthis &operator +=(const Tthis &rhs) { this->value += rhs.value; return *static_cast<Tthis *>(this); }
|
|
|
|
|
constexpr Tthis &operator -=(const Tthis &rhs) { this->value -= rhs.value; return *static_cast<Tthis *>(this); }
|
|
|
|
|
/** Non-templated base for #StrongType::Typedef for use with type trait queries. */
|
|
|
|
|
struct StrongTypedefBase {};
|
|
|
|
|
|
|
|
|
|
constexpr Tthis operator +(const Tthis &rhs) const { return Tthis{ this->value + rhs.value }; }
|
|
|
|
|
constexpr Tthis operator -(const Tthis &rhs) const { return Tthis{ this->value - rhs.value }; }
|
|
|
|
|
constexpr Tthis operator +(const T &rhs) const { return Tthis{ this->value + rhs }; }
|
|
|
|
|
constexpr Tthis operator -(const T &rhs) const { return Tthis{ this->value - rhs }; }
|
|
|
|
|
};
|
|
|
|
|
namespace StrongType {
|
|
|
|
|
/**
|
|
|
|
|
* Mix-in which makes the new Typedef comparable with itself and its base type.
|
|
|
|
|
*/
|
|
|
|
|
struct Compare {
|
|
|
|
|
template <typename TType, typename TBaseType>
|
|
|
|
|
struct mixin {
|
|
|
|
|
friend constexpr bool operator ==(const TType &lhs, const TType &rhs) { return lhs.value == rhs.value; }
|
|
|
|
|
friend constexpr bool operator ==(const TType &lhs, const TBaseType &rhs) { return lhs.value == rhs; }
|
|
|
|
|
|
|
|
|
|
friend constexpr bool operator !=(const TType &lhs, const TType &rhs) { return lhs.value != rhs.value; }
|
|
|
|
|
friend constexpr bool operator !=(const TType &lhs, const TBaseType &rhs) { return lhs.value != rhs; }
|
|
|
|
|
|
|
|
|
|
friend constexpr bool operator <=(const TType &lhs, const TType &rhs) { return lhs.value <= rhs.value; }
|
|
|
|
|
friend constexpr bool operator <=(const TType &lhs, const TBaseType &rhs) { return lhs.value <= rhs; }
|
|
|
|
|
|
|
|
|
|
friend constexpr bool operator <(const TType &lhs, const TType &rhs) { return lhs.value < rhs.value; }
|
|
|
|
|
friend constexpr bool operator <(const TType &lhs, const TBaseType &rhs) { return lhs.value < rhs; }
|
|
|
|
|
|
|
|
|
|
friend constexpr bool operator >=(const TType &lhs, const TType &rhs) { return lhs.value >= rhs.value; }
|
|
|
|
|
friend constexpr bool operator >=(const TType &lhs, const TBaseType &rhs) { return lhs.value >= rhs; }
|
|
|
|
|
|
|
|
|
|
friend constexpr bool operator >(const TType &lhs, const TType &rhs) { return lhs.value > rhs.value; }
|
|
|
|
|
friend constexpr bool operator >(const TType &lhs, const TBaseType &rhs) { return lhs.value > rhs; }
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mix-in which makes the new Typedef behave more like an integer. This means you can add and subtract from it.
|
|
|
|
|
*
|
|
|
|
|
* Operators like divide, multiply and module are explicitly denied, as that often makes little sense for the
|
|
|
|
|
* new type. If you want to do these actions on the new Typedef, you are better off first casting it to the
|
|
|
|
|
* base type.
|
|
|
|
|
*/
|
|
|
|
|
struct Integer {
|
|
|
|
|
template <typename TType, typename TBaseType>
|
|
|
|
|
struct mixin {
|
|
|
|
|
friend constexpr TType &operator ++(TType &lhs) { lhs.value++; return lhs; }
|
|
|
|
|
friend constexpr TType &operator --(TType &lhs) { lhs.value--; return lhs; }
|
|
|
|
|
friend constexpr TType operator ++(TType &lhs, int) { TType res = lhs; lhs.value++; return res; }
|
|
|
|
|
friend constexpr TType operator --(TType &lhs, int) { TType res = lhs; lhs.value--; return res; }
|
|
|
|
|
|
|
|
|
|
friend constexpr TType &operator +=(TType &lhs, const TType &rhs) { lhs.value += rhs.value; return lhs; }
|
|
|
|
|
friend constexpr TType operator +(const TType &lhs, const TType &rhs) { return TType{ lhs.value + rhs.value }; }
|
|
|
|
|
friend constexpr TType operator +(const TType &lhs, const TBaseType &rhs) { return TType{ lhs.value + rhs }; }
|
|
|
|
|
|
|
|
|
|
friend constexpr TType &operator -=(TType &lhs, const TType &rhs) { lhs.value -= rhs.value; return lhs; }
|
|
|
|
|
friend constexpr TType operator -(const TType &lhs, const TType &rhs) { return TType{ lhs.value - rhs.value }; }
|
|
|
|
|
friend constexpr TType operator -(const TType &lhs, const TBaseType &rhs) { return TType{ lhs.value - rhs }; }
|
|
|
|
|
|
|
|
|
|
/* For most new types, the rest of the operators make no sense. For example,
|
|
|
|
|
* what does it actually mean to multiply a Year with a value. Or to do a
|
|
|
|
|
* bitwise OR on a Date. Or to divide a TileIndex by 2. Conceptually, they
|
|
|
|
|
* don't really mean anything. So force the user to first cast it to the
|
|
|
|
|
* base type, so the operation no longer returns the new Typedef. */
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator *=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator *(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator *(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator /=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator /(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator /(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator %=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator %(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator %(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator &=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator &(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator &(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator |=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator |(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator |(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator ^=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator ^(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator ^(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator <<=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator <<(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator <<(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType &operator >>=(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator >>(const TType &rhs) = delete;
|
|
|
|
|
constexpr TType operator >>(const TBaseType &rhs) = delete;
|
|
|
|
|
|
|
|
|
|
constexpr TType operator ~() = delete;
|
|
|
|
|
constexpr TType operator -() = delete;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mix-in which makes the new Typedef compatible with another type (which is not the base type).
|
|
|
|
|
*
|
|
|
|
|
* @note The base type of the new Typedef will be cast to the other type; so make sure they are compatible.
|
|
|
|
|
*
|
|
|
|
|
* @tparam TCompatibleType The other type to be compatible with.
|
|
|
|
|
*/
|
|
|
|
|
template <typename TCompatibleType>
|
|
|
|
|
struct Compatible {
|
|
|
|
|
template <typename TType, typename TBaseType>
|
|
|
|
|
struct mixin {
|
|
|
|
|
friend constexpr bool operator ==(const TType &lhs, TCompatibleType rhs) { return lhs.value == static_cast<TBaseType>(rhs); }
|
|
|
|
|
friend constexpr bool operator !=(const TType &lhs, TCompatibleType rhs) { return lhs.value != static_cast<TBaseType>(rhs); }
|
|
|
|
|
|
|
|
|
|
friend constexpr bool operator <=(const TType &lhs, TCompatibleType rhs) { return lhs.value <= static_cast<TBaseType>(rhs); }
|
|
|
|
|
friend constexpr bool operator <(const TType &lhs, TCompatibleType rhs) { return lhs.value < static_cast<TBaseType>(rhs); }
|
|
|
|
|
friend constexpr bool operator >=(const TType &lhs, TCompatibleType rhs) { return lhs.value >= static_cast<TBaseType>(rhs); }
|
|
|
|
|
friend constexpr bool operator >(const TType &lhs, TCompatibleType rhs) { return lhs.value > static_cast<TBaseType>(rhs); }
|
|
|
|
|
|
|
|
|
|
friend constexpr TType operator +(const TType &lhs, TCompatibleType rhs) { return { static_cast<TBaseType>(lhs.value + rhs) }; }
|
|
|
|
|
friend constexpr TType operator -(const TType &lhs, TCompatibleType rhs) { return { static_cast<TBaseType>(lhs.value - rhs) }; }
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mix-in which makes the new Typedef implicitly convertible to its base type.
|
|
|
|
|
*
|
|
|
|
|
* Be careful: when allowing implicit conversion, you won't notice if this type is assigned to a compatible, but different, type.
|
|
|
|
|
* For example:
|
|
|
|
|
*
|
|
|
|
|
* StrongType::Typedef<int, struct MyTypeTag, Implicit> a = 1;
|
|
|
|
|
* StrongType::Typedef<int, struct MyTypeTag, Implicit> b = 2;
|
|
|
|
|
* a = b; // OK
|
|
|
|
|
*/
|
|
|
|
|
struct Implicit {
|
|
|
|
|
template <typename TType, typename TBaseType>
|
|
|
|
|
struct mixin {
|
|
|
|
|
constexpr operator TBaseType () const { return static_cast<const TType &>(*this).value; }
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mix-in which makes the new Typedef explicitly convertible to its base type.
|
|
|
|
|
*/
|
|
|
|
|
struct Explicit {
|
|
|
|
|
template <typename TType, typename TBaseType>
|
|
|
|
|
struct mixin {
|
|
|
|
|
explicit constexpr operator TBaseType () const { return static_cast<const TType &>(*this).value; }
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Templated helper to make a type-safe 'typedef' representing a single POD value.
|
|
|
|
|
* A normal 'typedef' is not distinct from its base type and will be treated as
|
|
|
|
|
* identical in many contexts. This class provides a distinct type that can still
|
|
|
|
|
* be assign from and compared to values of its base type.
|
|
|
|
|
*
|
|
|
|
|
* Example usage:
|
|
|
|
|
*
|
|
|
|
|
* using MyType = StrongType::Typedef<int, struct MyTypeTag, StrongType::Explicit, StrongType::Compare, StrongType::Integer>;
|
|
|
|
|
*
|
|
|
|
|
* @tparam TBaseType Type of the derived class (i.e. the concrete usage of this class).
|
|
|
|
|
* @tparam TTag An unique struct to keep types of the same TBaseType distinct.
|
|
|
|
|
* @tparam TProperties A list of mixins to add to the class.
|
|
|
|
|
*/
|
|
|
|
|
template <typename TBaseType, typename TTag, typename... TProperties>
|
|
|
|
|
struct EMPTY_BASES Typedef : public StrongTypedefBase, public TProperties::template mixin<Typedef<TBaseType, TTag, TProperties...>, TBaseType>... {
|
|
|
|
|
using BaseType = TBaseType;
|
|
|
|
|
|
|
|
|
|
constexpr Typedef() = default;
|
|
|
|
|
constexpr Typedef(const Typedef &) = default;
|
|
|
|
|
constexpr Typedef(Typedef &&) = default;
|
|
|
|
|
|
|
|
|
|
constexpr Typedef(const TBaseType &value) : value(value) {}
|
|
|
|
|
|
|
|
|
|
constexpr Typedef &operator =(const Typedef &rhs) { this->value = rhs.value; return *this; }
|
|
|
|
|
constexpr Typedef &operator =(Typedef &&rhs) { this->value = std::move(rhs.value); return *this; }
|
|
|
|
|
constexpr Typedef &operator =(const TBaseType &rhs) { this->value = rhs; return *this; }
|
|
|
|
|
|
|
|
|
|
/* Only allow TProperties classes access to the internal value. Everyone else needs to do an explicit cast. */
|
|
|
|
|
friend struct Explicit;
|
|
|
|
|
friend struct Implicit;
|
|
|
|
|
friend struct Compare;
|
|
|
|
|
friend struct Integer;
|
|
|
|
|
template <typename TCompatibleType> friend struct Compatible;
|
|
|
|
|
|
|
|
|
|
/* GCC / MSVC don't pick up on the "friend struct" above, where CLang does.
|
|
|
|
|
* As in our CI we compile for all three targets, it is sufficient to have one
|
|
|
|
|
* that errors on this; but nobody should be using "value" directly. Instead,
|
|
|
|
|
* use a static_cast<> to convert to the base type. */
|
|
|
|
|
#ifdef __clang__
|
|
|
|
|
protected:
|
|
|
|
|
#endif /* __clang__ */
|
|
|
|
|
TBaseType value{};
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif /* STRONG_TYPEDEF_TYPE_HPP */
|
|
|
|
|