Files
@ r28779:ec91f2ad2516
Branch filter:
Location: cpp/openttd-patchpack/source/src/core/strong_typedef_type.hpp
r28779:ec91f2ad2516
9.2 KiB
text/x-c++hdr
Update: Translations from eints
estonian: 15 changes by RM87
portuguese (brazilian): 42 changes by pasantoro
polish: 30 changes by pAter-exe
estonian: 15 changes by RM87
portuguese (brazilian): 42 changes by pasantoro
polish: 30 changes by pAter-exe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | /*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file strong_typedef_type.hpp Type (helpers) for making a strong typedef that is a distinct type. */
#ifndef STRONG_TYPEDEF_TYPE_HPP
#define STRONG_TYPEDEF_TYPE_HPP
#include "../3rdparty/fmt/format.h"
/** Non-templated base for #StrongType::Typedef for use with type trait queries. */
struct StrongTypedefBase {};
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) }; }
};
};
/**
* 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 conversion to BaseType via method. */
constexpr TBaseType base() const { return this->value; }
/* Only allow TProperties classes access to the internal value. Everyone else needs to call .base(). */
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 base() to convert to the base type. */
#ifdef __clang__
protected:
#endif /* __clang__ */
TBaseType value{};
};
}
#endif /* STRONG_TYPEDEF_TYPE_HPP */
|