Update to 14.0-beta1

This commit is contained in:
dP
2024-02-04 02:18:17 +05:30
parent 79037e2c65
commit 33ef333b57
1325 changed files with 138465 additions and 70987 deletions

View File

@@ -3,11 +3,11 @@ add_files(
alloc_func.hpp
alloc_type.hpp
backup_type.hpp
bitmath_func.cpp
bitmath_func.hpp
endian_func.hpp
endian_type.hpp
enum_type.hpp
format.hpp
geometry_func.cpp
geometry_func.hpp
geometry_type.hpp
@@ -22,11 +22,7 @@ add_files(
pool_type.hpp
random_func.cpp
random_func.hpp
smallmap_type.hpp
smallmatrix_type.hpp
smallstack_type.hpp
smallvec_type.hpp
span_type.hpp
string_compare_type.hpp
container_func.hpp
strong_typedef_type.hpp
)

View File

@@ -9,22 +9,24 @@
#include "../stdafx.h"
#include "../error_func.h"
#include "../safeguards.h"
/**
* Function to exit with an error message after malloc() or calloc() have failed
* @param size number of bytes we tried to allocate
*/
void NORETURN MallocError(size_t size)
[[noreturn]] void MallocError(size_t size)
{
error("Out of memory. Cannot allocate " PRINTF_SIZE " bytes", size);
FatalError("Out of memory. Cannot allocate {} bytes", size);
}
/**
* Function to exit with an error message after realloc() have failed
* @param size number of bytes we tried to allocate
*/
void NORETURN ReallocError(size_t size)
[[noreturn]] void ReallocError(size_t size)
{
error("Out of memory. Cannot reallocate " PRINTF_SIZE " bytes", size);
FatalError("Out of memory. Cannot reallocate {} bytes", size);
}

View File

@@ -17,8 +17,8 @@
* binary needlessly large.
*/
void NORETURN MallocError(size_t size);
void NORETURN ReallocError(size_t size);
[[noreturn]] void MallocError(size_t size);
[[noreturn]] void ReallocError(size_t size);
/**
* Checks whether allocating memory would overflow size_t.
@@ -26,7 +26,7 @@ void NORETURN ReallocError(size_t size);
* @param element_size Size of the structure to allocate.
* @param num_elements Number of elements to allocate.
*/
static inline void CheckAllocationConstraints(size_t element_size, size_t num_elements)
inline void CheckAllocationConstraints(size_t element_size, size_t num_elements)
{
if (num_elements > SIZE_MAX / element_size) MallocError(SIZE_MAX);
}
@@ -38,7 +38,7 @@ static inline void CheckAllocationConstraints(size_t element_size, size_t num_el
* @param num_elements Number of elements to allocate.
*/
template <typename T>
static inline void CheckAllocationConstraints(size_t num_elements)
inline void CheckAllocationConstraints(size_t num_elements)
{
CheckAllocationConstraints(sizeof(T), num_elements);
}
@@ -54,7 +54,7 @@ static inline void CheckAllocationConstraints(size_t num_elements)
* @return nullptr when num_elements == 0, non-nullptr otherwise.
*/
template <typename T>
static inline T *MallocT(size_t num_elements)
inline T *MallocT(size_t num_elements)
{
/*
* MorphOS cannot handle 0 elements allocations, or rather that always
@@ -82,7 +82,7 @@ static inline T *MallocT(size_t num_elements)
* @return nullptr when num_elements == 0, non-nullptr otherwise.
*/
template <typename T>
static inline T *CallocT(size_t num_elements)
inline T *CallocT(size_t num_elements)
{
/*
* MorphOS cannot handle 0 elements allocations, or rather that always
@@ -108,7 +108,7 @@ static inline T *CallocT(size_t num_elements)
* @return nullptr when num_elements == 0, non-nullptr otherwise.
*/
template <typename T>
static inline T *ReallocT(T *t_ptr, size_t num_elements)
inline T *ReallocT(T *t_ptr, size_t num_elements)
{
/*
* MorphOS cannot handle 0 elements allocations, or rather that always
@@ -128,9 +128,4 @@ static inline T *ReallocT(T *t_ptr, size_t num_elements)
return t_ptr;
}
/** alloca() has to be called in the parent function, so define AllocaM() as a macro */
#define AllocaM(T, num_elements) \
(CheckAllocationConstraints<T>(num_elements), \
(T*)alloca((num_elements) * sizeof(T)))
#endif /* ALLOC_FUNC_HPP */

View File

@@ -86,7 +86,7 @@ class ZeroedMemoryAllocator
{
public:
ZeroedMemoryAllocator() {}
virtual ~ZeroedMemoryAllocator() {}
virtual ~ZeroedMemoryAllocator() = default;
/**
* Memory allocator for a single class instance.

View File

@@ -144,4 +144,46 @@ private:
const int line;
};
/**
* Class to backup a specific variable and restore it upon destruction of this object to prevent
* stack values going out of scope before resetting the global to its original value. Contrary to
* #Backup this restores the variable automatically and there is no manual option to restore.
*/
template <typename T>
struct AutoRestoreBackup {
/*
* There is explicitly no only original constructor version, as that would make it possible
* for the new value to go out of scope before this object goes out of scope, thus defeating
* the whole goal and reason for existing of this object.
*/
/**
* Backup variable and switch to new value.
* @param original Variable to backup.
* @param new_value New value for variable.
*/
AutoRestoreBackup(T &original, T new_value) : original(original), original_value(original)
{
original = new_value;
}
/**
* Restore the variable upon object destruction.
*/
~AutoRestoreBackup()
{
this->original = this->original_value;
}
private:
T &original;
T original_value;
/* Prevent copy, assignment and allocation on stack. */
AutoRestoreBackup(const AutoRestoreBackup&) = delete;
AutoRestoreBackup& operator=(AutoRestoreBackup&) = delete;
static void *operator new(std::size_t) = delete;
static void *operator new[](std::size_t) = delete;
};
#endif /* BACKUP_TYPE_HPP */

View File

@@ -1,80 +0,0 @@
/*
* 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 bitmath_func.cpp Functions related to bit mathematics. */
#include "../stdafx.h"
#include "bitmath_func.hpp"
#include "../safeguards.h"
const uint8 _ffb_64[64] = {
0, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0,
5, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0,
4, 0, 1, 0, 2, 0, 1, 0,
3, 0, 1, 0, 2, 0, 1, 0,
};
/**
* Search the first set bit in a 64 bit variable.
*
* This algorithm is a static implementation of a log
* congruence search algorithm. It checks the first half
* if there is a bit set search there further. And this
* way further. If no bit is set return 0.
*
* @param x The value to search
* @return The position of the first bit set
*/
uint8 FindFirstBit(uint64 x)
{
if (x == 0) return 0;
/* The macro FIND_FIRST_BIT is better to use when your x is
not more than 128. */
uint8 pos = 0;
if ((x & 0xffffffffULL) == 0) { x >>= 32; pos += 32; }
if ((x & 0x0000ffffULL) == 0) { x >>= 16; pos += 16; }
if ((x & 0x000000ffULL) == 0) { x >>= 8; pos += 8; }
if ((x & 0x0000000fULL) == 0) { x >>= 4; pos += 4; }
if ((x & 0x00000003ULL) == 0) { x >>= 2; pos += 2; }
if ((x & 0x00000001ULL) == 0) { pos += 1; }
return pos;
}
/**
* Search the last set bit in a 64 bit variable.
*
* This algorithm is a static implementation of a log
* congruence search algorithm. It checks the second half
* if there is a bit set search there further. And this
* way further. If no bit is set return 0.
*
* @param x The value to search
* @return The position of the last bit set
*/
uint8 FindLastBit(uint64 x)
{
if (x == 0) return 0;
uint8 pos = 0;
if ((x & 0xffffffff00000000ULL) != 0) { x >>= 32; pos += 32; }
if ((x & 0x00000000ffff0000ULL) != 0) { x >>= 16; pos += 16; }
if ((x & 0x000000000000ff00ULL) != 0) { x >>= 8; pos += 8; }
if ((x & 0x00000000000000f0ULL) != 0) { x >>= 4; pos += 4; }
if ((x & 0x000000000000000cULL) != 0) { x >>= 2; pos += 2; }
if ((x & 0x0000000000000002ULL) != 0) { pos += 1; }
return pos;
}

View File

@@ -29,7 +29,7 @@
* @return The selected bits, aligned to a LSB.
*/
template <typename T>
static inline uint GB(const T x, const uint8 s, const uint8 n)
debug_inline constexpr static uint GB(const T x, const uint8_t s, const uint8_t n)
{
return (x >> s) & (((T)1U << n) - 1);
}
@@ -55,7 +55,7 @@ static inline uint GB(const T x, const uint8 s, const uint8 n)
* @return The new value of \a x
*/
template <typename T, typename U>
static inline T SB(T &x, const uint8 s, const uint8 n, const U d)
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
{
x &= (T)(~((((T)1U << n) - 1) << s));
x |= (T)(d << s);
@@ -80,7 +80,7 @@ static inline T SB(T &x, const uint8 s, const uint8 n, const U d)
* @return The new value of \a x
*/
template <typename T, typename U>
static inline T AB(T &x, const uint8 s, const uint8 n, const U i)
constexpr T AB(T &x, const uint8_t s, const uint8_t n, const U i)
{
const T mask = ((((T)1U << n) - 1) << s);
x = (T)((x & ~mask) | ((x + (i << s)) & mask));
@@ -100,7 +100,7 @@ static inline T AB(T &x, const uint8 s, const uint8 n, const U i)
* @return True if the bit is set, false else.
*/
template <typename T>
static inline bool HasBit(const T x, const uint8 y)
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
{
return (x & ((T)1U << y)) != 0;
}
@@ -118,7 +118,7 @@ static inline bool HasBit(const T x, const uint8 y)
* @return The new value of the old value with the bit set
*/
template <typename T>
static inline T SetBit(T &x, const uint8 y)
constexpr T SetBit(T &x, const uint8_t y)
{
return x = (T)(x | ((T)1U << y));
}
@@ -148,7 +148,7 @@ static inline T SetBit(T &x, const uint8 y)
* @return The new value of the old value with the bit cleared
*/
template <typename T>
static inline T ClrBit(T &x, const uint8 y)
constexpr T ClrBit(T &x, const uint8_t y)
{
return x = (T)(x & ~((T)1U << y));
}
@@ -178,52 +178,44 @@ static inline T ClrBit(T &x, const uint8 y)
* @return The new value of the old value with the bit toggled
*/
template <typename T>
static inline T ToggleBit(T &x, const uint8 y)
constexpr T ToggleBit(T &x, const uint8_t y)
{
return x = (T)(x ^ ((T)1U << y));
}
/** Lookup table to check which bit is set in a 6 bit variable */
extern const uint8 _ffb_64[64];
/**
* Returns the first non-zero bit in a 6-bit value (from right).
* Search the first set bit in a value.
* When no bit is set, it returns 0.
*
* Returns the position of the first bit that is not zero, counted from the
* LSB. Ie, 110100 returns 2, 000001 returns 0, etc. When x == 0 returns
* 0.
*
* @param x The 6-bit value to check the first zero-bit
* @return The first position of a bit started from the LSB or 0 if x is 0.
* @param x The value to search.
* @return The position of the first bit set.
*/
#define FIND_FIRST_BIT(x) _ffb_64[(x)]
/**
* Finds the position of the first non-zero bit in an integer.
*
* This function returns the position of the first bit set in the
* integer. It does only check the bits of the bitmask
* 0x3F3F (0011111100111111) and checks only the
* bits of the bitmask 0x3F00 if and only if the
* lower part 0x00FF is 0. This results the bits at 0x00C0 must
* be also zero to check the bits at 0x3F00.
*
* @param value The value to check the first bits
* @return The position of the first bit which is set
* @see FIND_FIRST_BIT
*/
static inline uint8 FindFirstBit2x64(const int value)
template <typename T>
constexpr uint8_t FindFirstBit(T x)
{
if ((value & 0xFF) == 0) {
return FIND_FIRST_BIT((value >> 8) & 0x3F) + 8;
if (x == 0) return 0;
if constexpr (std::is_enum_v<T>) {
return std::countr_zero<std::underlying_type_t<T>>(x);
} else {
return FIND_FIRST_BIT(value & 0x3F);
return std::countr_zero(x);
}
}
uint8 FindFirstBit(uint64 x);
uint8 FindLastBit(uint64 x);
/**
* Search the last set bit in a value.
* When no bit is set, it returns 0.
*
* @param x The value to search.
* @return The position of the last bit set.
*/
template <typename T>
constexpr uint8_t FindLastBit(T x)
{
if (x == 0) return 0;
return std::numeric_limits<T>::digits - std::countl_zero(x) - 1;
}
/**
* Clear the first bit in an integer.
@@ -236,7 +228,7 @@ uint8 FindLastBit(uint64 x);
* @return The new value with the first bit cleared
*/
template <typename T>
static inline T KillFirstBit(T value)
constexpr T KillFirstBit(T value)
{
return value &= (T)(value - 1);
}
@@ -248,20 +240,13 @@ static inline T KillFirstBit(T value)
* @return the number of bits.
*/
template <typename T>
static inline uint CountBits(T value)
constexpr uint CountBits(T value)
{
uint num;
/* This loop is only called once for every bit set by clearing the lowest
* bit in each loop. The number of bits is therefore equal to the number of
* times the loop was called. It was found at the following website:
* http://graphics.stanford.edu/~seander/bithacks.html */
for (num = 0; value != 0; num++) {
value &= (T)(value - 1);
if constexpr (std::is_enum_v<T>) {
return std::popcount<std::underlying_type_t<T>>(value);
} else {
return std::popcount(value);
}
return num;
}
/**
@@ -271,7 +256,7 @@ static inline uint CountBits(T value)
* @return does \a value have exactly 1 bit set?
*/
template <typename T>
static inline bool HasExactlyOneBit(T value)
constexpr bool HasExactlyOneBit(T value)
{
return value != 0 && (value & (value - 1)) == 0;
}
@@ -283,48 +268,16 @@ static inline bool HasExactlyOneBit(T value)
* @return does \a value have at most 1 bit set?
*/
template <typename T>
static inline bool HasAtMostOneBit(T value)
constexpr bool HasAtMostOneBit(T value)
{
return (value & (value - 1)) == 0;
}
/**
* ROtate \a x Left by \a n
*
* @note Assumes a byte has 8 bits
* @param x The value which we want to rotate
* @param n The number how many we want to rotate
* @pre n < sizeof(T) * 8
* @return A bit rotated number
*/
template <typename T>
static inline T ROL(const T x, const uint8 n)
{
if (n == 0) return x;
return (T)(x << n | x >> (sizeof(x) * 8 - n));
}
/**
* ROtate \a x Right by \a n
*
* @note Assumes a byte has 8 bits
* @param x The value which we want to rotate
* @param n The number how many we want to rotate
* @pre n < sizeof(T) * 8
* @return A bit rotated number
*/
template <typename T>
static inline T ROR(const T x, const uint8 n)
{
if (n == 0) return x;
return (T)(x >> n | x << (sizeof(x) * 8 - n));
}
/**
* Iterable ensemble of each set bit in a value.
* @tparam Tbitpos Type of the position variable.
* @tparam Tbitset Type of the bitset value.
*/
*/
template <typename Tbitpos = uint, typename Tbitset = uint>
struct SetBitIterator {
struct Iterator {
@@ -341,7 +294,7 @@ struct SetBitIterator {
bool operator==(const Iterator &other) const
{
return this->bitset == other.bitset && (this->bitset == 0 || this->bitpos == other.bitpos);
return this->bitset == other.bitset;
}
bool operator!=(const Iterator &other) const { return !(*this == other); }
Tbitpos operator*() const { return this->bitpos; }
@@ -352,12 +305,14 @@ struct SetBitIterator {
Tbitpos bitpos;
void Validate()
{
while (this->bitset != 0 && (this->bitset & 1) == 0) this->Next();
if (this->bitset != 0) {
typename std::make_unsigned<Tbitset>::type unsigned_value = this->bitset;
this->bitpos = static_cast<Tbitpos>(FindFirstBit(unsigned_value));
}
}
void Next()
{
this->bitset = static_cast<Tbitset>(this->bitset >> 1);
this->bitpos++;
this->bitset = KillFirstBit(this->bitset);
}
};
@@ -373,10 +328,10 @@ private:
#if defined(__APPLE__)
/* Make endian swapping use Apple's macros to increase speed
* (since it will use hardware swapping if available).
* Even though they should return uint16 and uint32, we get
* Even though they should return uint16_t and uint32_t, we get
* warnings if we don't cast those (why?) */
# define BSWAP32(x) (static_cast<uint32>(CFSwapInt32(x)))
# define BSWAP16(x) (static_cast<uint16>(CFSwapInt16(x)))
# define BSWAP32(x) (static_cast<uint32_t>(CFSwapInt32(x)))
# define BSWAP16(x) (static_cast<uint16_t>(CFSwapInt16(x)))
#elif defined(_MSC_VER)
/* MSVC has intrinsics for swapping, resulting in faster code */
# define BSWAP32(x) (_byteswap_ulong(x))
@@ -387,11 +342,11 @@ private:
* @param x the variable to bitswap
* @return the bitswapped value.
*/
static inline uint32 BSWAP32(uint32 x)
static inline uint32_t BSWAP32(uint32_t x)
{
#if !defined(__ICC) && defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ >= 3))
/* GCC >= 4.3 provides a builtin, resulting in faster code */
return static_cast<uint32>(__builtin_bswap32(static_cast<int32>(x)));
return static_cast<uint32_t>(__builtin_bswap32(static_cast<int32_t>(x)));
#else
return ((x >> 24) & 0xFF) | ((x >> 8) & 0xFF00) | ((x << 8) & 0xFF0000) | ((x << 24) & 0xFF000000);
#endif /* defined(__GNUC__) */
@@ -402,7 +357,7 @@ private:
* @param x the variable to bitswap
* @return the bitswapped value.
*/
static inline uint16 BSWAP16(uint16 x)
static inline uint16_t BSWAP16(uint16_t x)
{
return (x >> 8) | (x << 8);
}

View File

@@ -5,48 +5,45 @@
* 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 smallvec_type.hpp Simple vector class that allows allocating an item without the need to copy this->data needlessly. */
/** @file container_func.hpp Some simple functions to help with accessing containers. */
#ifndef SMALLVEC_TYPE_HPP
#define SMALLVEC_TYPE_HPP
#include "alloc_func.hpp"
#include "mem_func.hpp"
#include <vector>
#ifndef CONTAINER_FUNC_HPP
#define CONTAINER_FUNC_HPP
/**
* Helper function to append an item to a vector if it is not already contained
* Consider using std::set, std::unordered_set or std::flat_set in new code
* Helper function to append an item to a container if it is not already contained.
* The container must have a \c emplace_back function.
* Consider using std::set, std::unordered_set or std::flat_set in new code.
*
* @param vec A reference to the vector to be extended
* @param container A reference to the container to be extended
* @param item Reference to the item to be copy-constructed if not found
*
* @return Whether the item was already present
*/
template <typename T>
inline bool include(std::vector<T>& vec, const T &item)
template <typename Container>
inline bool include(Container &container, typename Container::const_reference &item)
{
const bool is_member = std::find(vec.begin(), vec.end(), item) != vec.end();
if (!is_member) vec.emplace_back(item);
const bool is_member = std::find(container.begin(), container.end(), item) != container.end();
if (!is_member) container.emplace_back(item);
return is_member;
}
/**
* Helper function to get the index of an item
* Consider using std::set, std::unordered_set or std::flat_set in new code
* Consider using std::set, std::unordered_set or std::flat_set in new code.
*
* @param vec A reference to the vector to be extended
* @param container A reference to the container to be searched.
* @param item Reference to the item to be search for
*
* @return Index of element if found, otherwise -1
*/
template <typename T>
int find_index(std::vector<T> const& vec, T const& item)
template <typename Container>
int find_index(Container const &container, typename Container::const_reference item)
{
auto const it = std::find(vec.begin(), vec.end(), item);
if (it != vec.end()) return it - vec.begin();
auto const it = std::find(container.begin(), container.end(), item);
if (it != container.end()) return std::distance(container.begin(), it);
return -1;
}
#endif /* SMALLVEC_TYPE_HPP */
#endif /* CONTAINER_FUNC_HPP */

View File

@@ -38,18 +38,4 @@
# define TO_LE32X(x) (x)
#endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
static inline uint16 ReadLE16Aligned(const void *x)
{
return FROM_LE16(*(const uint16*)x);
}
static inline uint16 ReadLE16Unaligned(const void *x)
{
#if OTTD_ALIGNMENT == 1
return ((const byte*)x)[0] | ((const byte*)x)[1] << 8;
#else
return FROM_LE16(*(const uint16*)x);
#endif /* OTTD_ALIGNMENT == 1 */
}
#endif /* ENDIAN_FUNC_HPP */

View File

@@ -10,14 +10,6 @@
#ifndef ENDIAN_TYPE_HPP
#define ENDIAN_TYPE_HPP
#if defined(ARM) || defined(__arm__) || defined(__alpha__)
/** The architecture requires aligned access. */
# define OTTD_ALIGNMENT 1
#else
/** The architecture does not require aligned access. */
# define OTTD_ALIGNMENT 0
#endif
/** Little endian builds use this for TTD_ENDIAN. */
#define TTD_LITTLE_ENDIAN 0
/** Big endian builds use this for TTD_ENDIAN. */

View File

@@ -28,44 +28,20 @@
/** Operators to allow to work with enum as with type safe bit set in C++ */
# define DECLARE_ENUM_AS_BIT_SET(mask_t) \
inline constexpr mask_t operator | (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type<mask_t>::type)m1 | (std::underlying_type<mask_t>::type)m2);} \
inline constexpr mask_t operator & (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type<mask_t>::type)m1 & (std::underlying_type<mask_t>::type)m2);} \
inline constexpr mask_t operator ^ (mask_t m1, mask_t m2) {return (mask_t)((std::underlying_type<mask_t>::type)m1 ^ (std::underlying_type<mask_t>::type)m2);} \
inline constexpr mask_t& operator |= (mask_t& m1, mask_t m2) {m1 = m1 | m2; return m1;} \
inline constexpr mask_t& operator &= (mask_t& m1, mask_t m2) {m1 = m1 & m2; return m1;} \
inline constexpr mask_t& operator ^= (mask_t& m1, mask_t m2) {m1 = m1 ^ m2; return m1;} \
inline constexpr mask_t operator ~(mask_t m) {return (mask_t)(~(std::underlying_type<mask_t>::type)m);}
#define DECLARE_ENUM_AS_BIT_SET(enum_type) \
inline constexpr enum_type operator | (enum_type m1, enum_type m2) {return (enum_type)((std::underlying_type<enum_type>::type)m1 | (std::underlying_type<enum_type>::type)m2);} \
inline constexpr enum_type operator & (enum_type m1, enum_type m2) {return (enum_type)((std::underlying_type<enum_type>::type)m1 & (std::underlying_type<enum_type>::type)m2);} \
inline constexpr enum_type operator ^ (enum_type m1, enum_type m2) {return (enum_type)((std::underlying_type<enum_type>::type)m1 ^ (std::underlying_type<enum_type>::type)m2);} \
inline constexpr enum_type& operator |= (enum_type& m1, enum_type m2) {m1 = m1 | m2; return m1;} \
inline constexpr enum_type& operator &= (enum_type& m1, enum_type m2) {m1 = m1 & m2; return m1;} \
inline constexpr enum_type& operator ^= (enum_type& m1, enum_type m2) {m1 = m1 ^ m2; return m1;} \
inline constexpr enum_type operator ~(enum_type m) {return (enum_type)(~(std::underlying_type<enum_type>::type)m);}
/**
* Informative template class exposing basic enumeration properties used by several
* other templates below. Here we have only forward declaration. For each enum type
* we will create specialization derived from MakeEnumPropsT<>.
* i.e.:
* template <> struct EnumPropsT<Track> : MakeEnumPropsT<Track, byte, TRACK_BEGIN, TRACK_END, INVALID_TRACK> {};
*/
template <typename Tenum_t> struct EnumPropsT;
/**
* Helper template class that makes basic properties of given enumeration type visible
* from outsize. It is used as base class of several EnumPropsT specializations each
* dedicated to one of commonly used enumeration types.
* @param Tenum_t enumeration type that you want to describe
* @param Tstorage_t what storage type would be sufficient (i.e. byte)
* @param Tbegin first valid value from the contiguous range (i.e. TRACK_BEGIN)
* @param Tend one past the last valid value from the contiguous range (i.e. TRACK_END)
* @param Tinvalid value used as invalid value marker (i.e. INVALID_TRACK)
* @param Tnum_bits Number of bits for storing the enum in command parameters
*/
template <typename Tenum_t, typename Tstorage_t, Tenum_t Tbegin, Tenum_t Tend, Tenum_t Tinvalid, uint Tnum_bits = 8 * sizeof(Tstorage_t)>
struct MakeEnumPropsT {
typedef Tenum_t type; ///< enum type (i.e. Trackdir)
typedef Tstorage_t storage; ///< storage type (i.e. byte)
static const Tenum_t begin = Tbegin; ///< lowest valid value (i.e. TRACKDIR_BEGIN)
static const Tenum_t end = Tend; ///< one after the last valid value (i.e. TRACKDIR_END)
static const Tenum_t invalid = Tinvalid; ///< what value is used as invalid value (i.e. INVALID_TRACKDIR)
static const uint num_bits = Tnum_bits; ///< Number of bits for storing the enum in command parameters
};
/** Operator that allows this enumeration to be added to any other enumeration. */
#define DECLARE_ENUM_AS_ADDABLE(EnumType) \
template <typename OtherEnumType, typename = typename std::enable_if<std::is_enum_v<OtherEnumType>, OtherEnumType>::type> \
constexpr OtherEnumType operator + (OtherEnumType m1, EnumType m2) { \
return static_cast<OtherEnumType>(static_cast<typename std::underlying_type<OtherEnumType>::type>(m1) + static_cast<typename std::underlying_type<EnumType>::type>(m2)); \
}
#endif /* ENUM_TYPE_HPP */

48
src/core/format.hpp Normal file
View File

@@ -0,0 +1,48 @@
/*
* 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 format.hpp String formatting functions and helpers. */
#ifndef FORMAT_HPP
#define FORMAT_HPP
#include "../3rdparty/fmt/format.h"
#include "strong_typedef_type.hpp"
template <typename E, typename Char>
struct fmt::formatter<E, Char, std::enable_if_t<std::is_enum<E>::value>> : fmt::formatter<typename std::underlying_type<E>::type> {
using underlying_type = typename std::underlying_type<E>::type;
using parent = typename fmt::formatter<underlying_type>;
constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context &ctx)
{
return parent::parse(ctx);
}
fmt::format_context::iterator format(const E &e, format_context &ctx) const
{
return parent::format(underlying_type(e), ctx);
}
};
template <typename T, typename Char>
struct fmt::formatter<T, Char, std::enable_if_t<std::is_base_of<StrongTypedefBase, T>::value>> : fmt::formatter<typename T::BaseType> {
using underlying_type = typename T::BaseType;
using parent = typename fmt::formatter<underlying_type>;
constexpr fmt::format_parse_context::iterator parse(fmt::format_parse_context &ctx)
{
return parent::parse(ctx);
}
fmt::format_context::iterator format(const T &t, format_context &ctx) const
{
return parent::format(t.base(), ctx);
}
};
#endif /* FORMAT_HPP */

View File

@@ -48,5 +48,3 @@ Rect BoundingRect(const Rect &r1, const Rect &r2)
return r;
}
const RectPadding RectPadding::zero = {0, 0, 0, 0};

View File

@@ -19,7 +19,7 @@ Dimension maxdim(const Dimension &d1, const Dimension &d2);
* @param r Rectangle to check.
* @return True if and only if the rectangle doesn't define space.
*/
static inline bool IsEmptyRect(const Rect &r)
inline bool IsEmptyRect(const Rect &r)
{
return (r.left | r.top | r.right | r.bottom) == 0;
}

View File

@@ -21,6 +21,9 @@
struct Point {
int x;
int y;
constexpr Point() : x(0), y(0) {}
constexpr Point(int x, int y) : x(x), y(y) {}
};
/** Dimensions (a width and height) of a rectangle in 2D */
@@ -28,7 +31,8 @@ struct Dimension {
uint width;
uint height;
Dimension(uint w = 0, uint h = 0) : width(w), height(h) {};
constexpr Dimension() : width(0), height(0) {}
constexpr Dimension(uint w, uint h) : width(w), height(h) {}
bool operator< (const Dimension &other) const
{
@@ -45,10 +49,10 @@ struct Dimension {
/** Padding dimensions to apply to each side of a Rect. */
struct RectPadding {
uint8 left;
uint8 top;
uint8 right;
uint8 bottom;
uint8_t left;
uint8_t top;
uint8_t right;
uint8_t bottom;
static const RectPadding zero;
@@ -56,15 +60,17 @@ struct RectPadding {
* Get total horizontal padding of RectPadding.
* @return total horizontal padding.
*/
inline uint Horizontal() const { return this->left + this->right; }
constexpr uint Horizontal() const { return this->left + this->right; }
/**
* Get total vertical padding of RectPadding.
* @return total vertical padding.
*/
inline uint Vertical() const { return this->top + this->bottom; }
constexpr uint Vertical() const { return this->top + this->bottom; }
};
inline const RectPadding RectPadding::zero{};
/** Specification of a rectangle with absolute coordinates of all edges */
struct Rect {
int left;
@@ -212,7 +218,7 @@ struct Rect {
/**
* Test if a point falls inside this Rect.
* @param pt the point to test.
* @return true iif the point falls inside the Rect.
* @return true iff the point falls inside the Rect.
*/
inline bool Contains(const Point &pt) const
{

View File

@@ -11,8 +11,6 @@
#define KDTREE_HPP
#include "../stdafx.h"
#include <vector>
#include <limits>
/**
* K-dimensional tree, specialised for 2-dimensional space.
@@ -55,7 +53,7 @@ class Kdtree {
/** Create one new node in the tree, return its index in the pool */
size_t AddNode(const T &element)
{
if (this->free_list.size() == 0) {
if (this->free_list.empty()) {
this->nodes.emplace_back(element);
return this->nodes.size() - 1;
} else {
@@ -449,14 +447,14 @@ public:
}
/**
* Find all items contained within the given rectangle.
* @note Start coordinates are inclusive, end coordinates are exclusive. x1<x2 && y1<y2 is a precondition.
* @param x1 Start first coordinate, points found are greater or equals to this.
* @param y1 Start second coordinate, points found are greater or equals to this.
* @param x2 End first coordinate, points found are less than this.
* @param y2 End second coordinate, points found are less than this.
* @param outputter Callback used to return values from the search.
*/
* Find all items contained within the given rectangle.
* @note Start coordinates are inclusive, end coordinates are exclusive. x1<x2 && y1<y2 is a precondition.
* @param x1 Start first coordinate, points found are greater or equals to this.
* @param y1 Start second coordinate, points found are greater or equals to this.
* @param x2 End first coordinate, points found are less than this.
* @param y2 End second coordinate, points found are less than this.
* @param outputter Callback used to return values from the search.
*/
template <typename Outputter>
void FindContained(CoordT x1, CoordT y1, CoordT x2, CoordT y2, const Outputter &outputter) const
{

View File

@@ -12,41 +12,6 @@
#include "../safeguards.h"
/**
* Compute least common multiple (lcm) of arguments \a a and \a b, the smallest
* integer value that is a multiple of both \a a and \a b.
* @param a First number.
* @param b second number.
* @return Least common multiple of values \a a and \a b.
*
* @note This function only works for non-negative values of \a a and \a b.
*/
int LeastCommonMultiple(int a, int b)
{
if (a == 0 || b == 0) return 0; // By definition.
if (a == 1 || a == b) return b;
if (b == 1) return a;
return a * b / GreatestCommonDivisor(a, b);
}
/**
* Compute greatest common divisor (gcd) of \a a and \a b.
* @param a First number.
* @param b second number.
* @return Greatest common divisor of \a a and \a b.
*/
int GreatestCommonDivisor(int a, int b)
{
while (b != 0) {
int t = b;
b = a % b;
a = t;
}
return a;
}
/**
* Deterministic approximate division.
* Cancels out division errors stemming from the integer nature of the division over multiple runs.
@@ -74,10 +39,10 @@ int DivideApprox(int a, int b)
* @return Rounded integer square root.
* @note Algorithm taken from http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
*/
uint32 IntSqrt(uint32 num)
uint32_t IntSqrt(uint32_t num)
{
uint32 res = 0;
uint32 bit = 1UL << 30; // Second to top bit number.
uint32_t res = 0;
uint32_t bit = 1UL << 30; // Second to top bit number.
/* 'bit' starts at the highest power of four <= the argument. */
while (bit > num) bit >>= 2;

View File

@@ -10,6 +10,8 @@
#ifndef MATH_FUNC_HPP
#define MATH_FUNC_HPP
#include "strong_typedef_type.hpp"
/**
* Returns the absolute value of (scalar) variable.
*
@@ -18,7 +20,7 @@
* @return The unsigned value
*/
template <typename T>
static inline T abs(const T a)
constexpr T abs(const T a)
{
return (a < (T)0) ? -a : a;
}
@@ -32,7 +34,7 @@ static inline T abs(const T a)
* @return The smallest multiple of n equal or greater than x
*/
template <typename T>
static inline T Align(const T x, uint n)
constexpr T Align(const T x, uint n)
{
assert((n & (n - 1)) == 0 && n != 0);
n--;
@@ -50,7 +52,7 @@ static inline T Align(const T x, uint n)
* @see Align()
*/
template <typename T>
static inline T *AlignPtr(T *x, uint n)
constexpr T *AlignPtr(T *x, uint n)
{
static_assert(sizeof(size_t) == sizeof(void *));
return reinterpret_cast<T *>(Align((size_t)x, n));
@@ -74,7 +76,7 @@ static inline T *AlignPtr(T *x, uint n)
* @see Clamp(int, int, int)
*/
template <typename T>
static inline T Clamp(const T a, const T min, const T max)
constexpr T Clamp(const T a, const T min, const T max)
{
assert(min <= max);
if (a <= min) return min;
@@ -97,7 +99,7 @@ static inline T Clamp(const T a, const T min, const T max)
* @returns A value between min and max which is closest to a.
*/
template <typename T>
static inline T SoftClamp(const T a, const T min, const T max)
constexpr T SoftClamp(const T a, const T min, const T max)
{
if (min > max) {
using U = std::make_unsigned_t<T>;
@@ -124,7 +126,7 @@ static inline T SoftClamp(const T a, const T min, const T max)
* @returns A value between min and max which is closest to a.
* @see ClampU(uint, uint, uint)
*/
static inline int Clamp(const int a, const int min, const int max)
constexpr int Clamp(const int a, const int min, const int max)
{
return Clamp<int>(a, min, max);
}
@@ -145,44 +147,80 @@ static inline int Clamp(const int a, const int min, const int max)
* @returns A value between min and max which is closest to a.
* @see Clamp(int, int, int)
*/
static inline uint ClampU(const uint a, const uint min, const uint max)
constexpr uint ClampU(const uint a, const uint min, const uint max)
{
return Clamp<uint>(a, min, max);
}
/**
* Reduce a signed 64-bit int to a signed 32-bit one
* Clamp the given value down to lie within the requested type.
*
* This function clamps a 64-bit integer to a 32-bit integer.
* If the 64-bit value is smaller than the smallest 32-bit integer
* value 0x80000000 this value is returned (the left one bit is the sign bit).
* If the 64-bit value is greater than the greatest 32-bit integer value 0x7FFFFFFF
* this value is returned. In all other cases the 64-bit value 'fits' in a
* 32-bits integer field and so the value is casted to int32 and returned.
* For example ClampTo<uint8_t> will return a value clamped to the range of 0
* to 255. Anything smaller will become 0, anything larger will become 255.
*
* @param a The 64-bit value to clamps
* @return The 64-bit value reduced to a 32-bit value
* @param a The 64-bit value to clamp.
* @return The 64-bit value reduced to a value within the given allowed range
* for the return type.
* @see Clamp(int, int, int)
*/
static inline int32 ClampToI32(const int64 a)
template <typename To, typename From, std::enable_if_t<std::is_integral<From>::value, int> = 0>
constexpr To ClampTo(From value)
{
return static_cast<int32>(Clamp<int64>(a, INT32_MIN, INT32_MAX));
static_assert(std::numeric_limits<To>::is_integer, "Do not clamp from non-integer values");
static_assert(std::numeric_limits<From>::is_integer, "Do not clamp to non-integer values");
if constexpr (sizeof(To) >= sizeof(From) && std::numeric_limits<To>::is_signed == std::numeric_limits<From>::is_signed) {
/* Same signedness and To type is larger or equal than From type, no clamping is required. */
return static_cast<To>(value);
}
if constexpr (sizeof(To) > sizeof(From) && std::numeric_limits<To>::is_signed) {
/* Signed destination and a larger To type, no clamping is required. */
return static_cast<To>(value);
}
/* Get the bigger of the two types based on essentially the number of bits. */
using BiggerType = typename std::conditional<sizeof(From) >= sizeof(To), From, To>::type;
if constexpr (std::numeric_limits<To>::is_signed) {
/* The output is a signed number. */
if constexpr (std::numeric_limits<From>::is_signed) {
/* Both input and output are signed. */
return static_cast<To>(std::clamp<BiggerType>(value,
std::numeric_limits<To>::lowest(), std::numeric_limits<To>::max()));
}
/* The input is unsigned, so skip the minimum check and use unsigned variant of the biggest type as intermediate type. */
using BiggerUnsignedType = typename std::make_unsigned<BiggerType>::type;
return static_cast<To>(std::min<BiggerUnsignedType>(std::numeric_limits<To>::max(), value));
}
/* The output is unsigned. */
if constexpr (std::numeric_limits<From>::is_signed) {
/* Input is signed; account for the negative numbers in the input. */
if constexpr (sizeof(To) >= sizeof(From)) {
/* If the output type is larger or equal to the input type, then only clamp the negative numbers. */
return static_cast<To>(std::max<From>(value, 0));
}
/* The output type is smaller than the input type. */
using BiggerSignedType = typename std::make_signed<BiggerType>::type;
return static_cast<To>(std::clamp<BiggerSignedType>(value,
std::numeric_limits<To>::lowest(), std::numeric_limits<To>::max()));
}
/* The input and output are unsigned, just clamp at the high side. */
return static_cast<To>(std::min<BiggerType>(value, std::numeric_limits<To>::max()));
}
/**
* Reduce an unsigned 64-bit int to an unsigned 16-bit one
*
* @param a The 64-bit value to clamp
* @return The 64-bit value reduced to a 16-bit value
* @see ClampU(uint, uint, uint)
* Specialization of ClampTo for #StrongType::Typedef.
*/
static inline uint16 ClampToU16(const uint64 a)
template <typename To, typename From, std::enable_if_t<std::is_base_of<StrongTypedefBase, From>::value, int> = 0>
constexpr To ClampTo(From value)
{
/* MSVC thinks, in its infinite wisdom, that int min(int, int) is a better
* match for min(uint64, uint) than uint64 min(uint64, uint64). As such we
* need to cast the UINT16_MAX to prevent MSVC from displaying its
* infinite loads of warnings. */
return static_cast<uint16>(std::min(a, static_cast<uint64>(UINT16_MAX)));
return ClampTo<To>(value.base());
}
/**
@@ -193,7 +231,7 @@ static inline uint16 ClampToU16(const uint64 a)
* @return The absolute difference between the given scalars
*/
template <typename T>
static inline T Delta(const T a, const T b)
constexpr T Delta(const T a, const T b)
{
return (a < b) ? b - a : a - b;
}
@@ -211,7 +249,7 @@ static inline T Delta(const T a, const T b)
* @return True if the value is in the interval, false else.
*/
template <typename T>
static inline bool IsInsideBS(const T x, const size_t base, const size_t size)
constexpr bool IsInsideBS(const T x, const size_t base, const size_t size)
{
return (size_t)(x - base) < size;
}
@@ -226,10 +264,14 @@ static inline bool IsInsideBS(const T x, const size_t base, const size_t size)
* @param max The maximum of the interval
* @see IsInsideBS()
*/
template <typename T>
static constexpr inline bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
template <typename T, std::enable_if_t<std::disjunction_v<std::is_convertible<T, size_t>, std::is_base_of<StrongTypedefBase, T>>, int> = 0>
constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
{
return (size_t)(x - min) < (max - min);
if constexpr (std::is_base_of_v<StrongTypedefBase, T>) {
return (size_t)(x.base() - min) < (max - min);
} else {
return (size_t)(x - min) < (max - min);
}
}
/**
@@ -238,7 +280,7 @@ static constexpr inline bool IsInsideMM(const T x, const size_t min, const size_
* @param b variable to swap with a
*/
template <typename T>
static inline void Swap(T &a, T &b)
constexpr void Swap(T &a, T &b)
{
T t = a;
a = b;
@@ -250,7 +292,7 @@ static inline void Swap(T &a, T &b)
* @param i value to convert, range 0..255
* @return value in range 0..100
*/
static inline uint ToPercent8(uint i)
constexpr uint ToPercent8(uint i)
{
assert(i < 256);
return i * 101 >> 8;
@@ -261,14 +303,12 @@ static inline uint ToPercent8(uint i)
* @param i value to convert, range 0..65535
* @return value in range 0..100
*/
static inline uint ToPercent16(uint i)
constexpr uint ToPercent16(uint i)
{
assert(i < 65536);
return i * 101 >> 16;
}
int LeastCommonMultiple(int a, int b);
int GreatestCommonDivisor(int a, int b);
int DivideApprox(int a, int b);
/**
@@ -277,7 +317,7 @@ int DivideApprox(int a, int b);
* @param b Denominator
* @return Quotient, rounded up
*/
static inline uint CeilDiv(uint a, uint b)
constexpr uint CeilDiv(uint a, uint b)
{
return (a + b - 1) / b;
}
@@ -288,7 +328,7 @@ static inline uint CeilDiv(uint a, uint b)
* @param b Denominator
* @return a rounded up to the nearest multiple of b.
*/
static inline uint Ceil(uint a, uint b)
constexpr uint Ceil(uint a, uint b)
{
return CeilDiv(a, b) * b;
}
@@ -299,7 +339,7 @@ static inline uint Ceil(uint a, uint b)
* @param b Denominator
* @return Quotient, rounded to nearest
*/
static inline int RoundDivSU(int a, uint b)
constexpr int RoundDivSU(int a, uint b)
{
if (a > 0) {
/* 0.5 is rounded to 1 */
@@ -310,23 +350,6 @@ static inline int RoundDivSU(int a, uint b)
}
}
/**
* Computes (a / b) rounded away from zero.
* @param a Numerator
* @param b Denominator
* @return Quotient, rounded away from zero
*/
static inline int DivAwayFromZero(int a, uint b)
{
const int _b = static_cast<int>(b);
if (a > 0) {
return (a + _b - 1) / _b;
} else {
/* Note: Behaviour of negative numerator division is truncation toward zero. */
return (a - _b + 1) / _b;
}
}
uint32 IntSqrt(uint32 num);
uint32_t IntSqrt(uint32_t num);
#endif /* MATH_FUNC_HPP */

View File

@@ -20,7 +20,7 @@
* @param num number of items to be copied. (!not number of bytes!)
*/
template <typename T>
static inline void MemCpyT(T *destination, const T *source, size_t num = 1)
inline void MemCpyT(T *destination, const T *source, size_t num = 1)
{
memcpy(destination, source, num * sizeof(T));
}
@@ -33,7 +33,7 @@ static inline void MemCpyT(T *destination, const T *source, size_t num = 1)
* @param num number of items to be copied. (!not number of bytes!)
*/
template <typename T>
static inline void MemMoveT(T *destination, const T *source, size_t num = 1)
inline void MemMoveT(T *destination, const T *source, size_t num = 1)
{
memmove(destination, source, num * sizeof(T));
}
@@ -46,7 +46,7 @@ static inline void MemMoveT(T *destination, const T *source, size_t num = 1)
* @param num number of items to be set (!not number of bytes!)
*/
template <typename T>
static inline void MemSetT(T *ptr, byte value, size_t num = 1)
inline void MemSetT(T *ptr, byte value, size_t num = 1)
{
memset(ptr, value, num * sizeof(T));
}
@@ -60,42 +60,9 @@ static inline void MemSetT(T *ptr, byte value, size_t num = 1)
* @return an int value indicating the relationship between the content of the two buffers
*/
template <typename T>
static inline int MemCmpT(const T *ptr1, const T *ptr2, size_t num = 1)
inline int MemCmpT(const T *ptr1, const T *ptr2, size_t num = 1)
{
return memcmp(ptr1, ptr2, num * sizeof(T));
}
/**
* Type safe memory reverse operation.
* Reverse a block of memory in steps given by the
* type of the pointers.
*
* @param ptr1 Start-pointer to the block of memory.
* @param ptr2 End-pointer to the block of memory.
*/
template <typename T>
static inline void MemReverseT(T *ptr1, T *ptr2)
{
assert(ptr1 != nullptr && ptr2 != nullptr);
assert(ptr1 < ptr2);
do {
Swap(*ptr1, *ptr2);
} while (++ptr1 < --ptr2);
}
/**
* Type safe memory reverse operation (overloaded)
*
* @param ptr Pointer to the block of memory.
* @param num The number of items we want to reverse.
*/
template <typename T>
static inline void MemReverseT(T *ptr, size_t num)
{
assert(ptr != nullptr);
MemReverseT(ptr, ptr + (num - 1));
}
#endif /* MEM_FUNC_HPP */

View File

@@ -10,9 +10,6 @@
#ifndef MULTIMAP_HPP
#define MULTIMAP_HPP
#include <map>
#include <list>
template<typename Tkey, typename Tvalue, typename Tcompare>
class MultiMap;

View File

@@ -12,7 +12,6 @@
#include "math_func.hpp"
#include <limits>
#ifdef __has_builtin
# if __has_builtin(__builtin_add_overflow) && __has_builtin(__builtin_sub_overflow) && __has_builtin(__builtin_mul_overflow)
@@ -38,7 +37,7 @@ private:
public:
constexpr OverflowSafeInt() : m_value(0) { }
constexpr OverflowSafeInt(const OverflowSafeInt& other) : m_value(other.m_value) { }
constexpr OverflowSafeInt(const OverflowSafeInt &other) : m_value(other.m_value) { }
constexpr OverflowSafeInt(const T int_) : m_value(int_) { }
inline constexpr OverflowSafeInt& operator = (const OverflowSafeInt& other) { this->m_value = other.m_value; return *this; }
@@ -54,7 +53,7 @@ public:
inline constexpr OverflowSafeInt& operator += (const OverflowSafeInt& other)
{
#ifdef HAS_OVERFLOW_BUILTINS
if (unlikely(__builtin_add_overflow(this->m_value, other.m_value, &this->m_value))) {
if (__builtin_add_overflow(this->m_value, other.m_value, &this->m_value)) [[unlikely]] {
this->m_value = (other.m_value < 0) ? T_MIN : T_MAX;
}
#else
@@ -77,7 +76,7 @@ public:
inline constexpr OverflowSafeInt& operator -= (const OverflowSafeInt& other)
{
#ifdef HAS_OVERFLOW_BUILTINS
if (unlikely(__builtin_sub_overflow(this->m_value, other.m_value, &this->m_value))) {
if (__builtin_sub_overflow(this->m_value, other.m_value, &this->m_value)) [[unlikely]] {
this->m_value = (other.m_value < 0) ? T_MAX : T_MIN;
}
#else
@@ -94,11 +93,11 @@ public:
/* Operators for addition and subtraction. */
inline constexpr OverflowSafeInt operator + (const OverflowSafeInt& other) const { OverflowSafeInt result = *this; result += other; return result; }
inline constexpr OverflowSafeInt operator + (const int other) const { OverflowSafeInt result = *this; result += (int64)other; return result; }
inline constexpr OverflowSafeInt operator + (const uint other) const { OverflowSafeInt result = *this; result += (int64)other; return result; }
inline constexpr OverflowSafeInt operator + (const int other) const { OverflowSafeInt result = *this; result += (int64_t)other; return result; }
inline constexpr OverflowSafeInt operator + (const uint other) const { OverflowSafeInt result = *this; result += (int64_t)other; return result; }
inline constexpr OverflowSafeInt operator - (const OverflowSafeInt& other) const { OverflowSafeInt result = *this; result -= other; return result; }
inline constexpr OverflowSafeInt operator - (const int other) const { OverflowSafeInt result = *this; result -= (int64)other; return result; }
inline constexpr OverflowSafeInt operator - (const uint other) const { OverflowSafeInt result = *this; result -= (int64)other; return result; }
inline constexpr OverflowSafeInt operator - (const int other) const { OverflowSafeInt result = *this; result -= (int64_t)other; return result; }
inline constexpr OverflowSafeInt operator - (const uint other) const { OverflowSafeInt result = *this; result -= (int64_t)other; return result; }
inline constexpr OverflowSafeInt& operator ++ () { return *this += 1; }
inline constexpr OverflowSafeInt& operator -- () { return *this += -1; }
@@ -115,7 +114,7 @@ public:
{
#ifdef HAS_OVERFLOW_BUILTINS
const bool is_result_positive = (this->m_value < 0) == (factor < 0); // -ve * -ve == +ve
if (unlikely(__builtin_mul_overflow(this->m_value, factor, &this->m_value))) {
if (__builtin_mul_overflow(this->m_value, factor, &this->m_value)) [[unlikely]] {
this->m_value = is_result_positive ? T_MAX : T_MIN;
}
#else
@@ -137,14 +136,14 @@ public:
}
/* Operators for multiplication. */
inline constexpr OverflowSafeInt operator * (const int64 factor) const { OverflowSafeInt result = *this; result *= factor; return result; }
inline constexpr OverflowSafeInt operator * (const int factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; }
inline constexpr OverflowSafeInt operator * (const uint factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; }
inline constexpr OverflowSafeInt operator * (const uint16 factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; }
inline constexpr OverflowSafeInt operator * (const byte factor) const { OverflowSafeInt result = *this; result *= (int64)factor; return result; }
inline constexpr OverflowSafeInt operator * (const int64_t factor) const { OverflowSafeInt result = *this; result *= factor; return result; }
inline constexpr OverflowSafeInt operator * (const int factor) const { OverflowSafeInt result = *this; result *= (int64_t)factor; return result; }
inline constexpr OverflowSafeInt operator * (const uint factor) const { OverflowSafeInt result = *this; result *= (int64_t)factor; return result; }
inline constexpr OverflowSafeInt operator * (const uint16_t factor) const { OverflowSafeInt result = *this; result *= (int64_t)factor; return result; }
inline constexpr OverflowSafeInt operator * (const byte factor) const { OverflowSafeInt result = *this; result *= (int64_t)factor; return result; }
/* Operators for division. */
inline constexpr OverflowSafeInt& operator /= (const int64 divisor) { this->m_value /= divisor; return *this; }
inline constexpr OverflowSafeInt& operator /= (const int64_t divisor) { this->m_value /= divisor; return *this; }
inline constexpr OverflowSafeInt operator / (const OverflowSafeInt& divisor) const { OverflowSafeInt result = *this; result /= divisor.m_value; return result; }
inline constexpr OverflowSafeInt operator / (const int divisor) const { OverflowSafeInt result = *this; result /= divisor; return result; }
inline constexpr OverflowSafeInt operator / (const uint divisor) const { OverflowSafeInt result = *this; result /= (int)divisor; return result; }
@@ -182,11 +181,11 @@ public:
};
/* Sometimes we got int64 operator OverflowSafeInt instead of vice versa. Handle that properly. */
template <class T> inline constexpr OverflowSafeInt<T> operator + (const int64 a, const OverflowSafeInt<T> b) { return b + a; }
template <class T> inline constexpr OverflowSafeInt<T> operator - (const int64 a, const OverflowSafeInt<T> b) { return -b + a; }
template <class T> inline constexpr OverflowSafeInt<T> operator * (const int64 a, const OverflowSafeInt<T> b) { return b * a; }
template <class T> inline constexpr OverflowSafeInt<T> operator / (const int64 a, const OverflowSafeInt<T> b) { return (OverflowSafeInt<T>)a / (int)b; }
/* Sometimes we got int64_t operator OverflowSafeInt instead of vice versa. Handle that properly. */
template <class T> inline constexpr OverflowSafeInt<T> operator + (const int64_t a, const OverflowSafeInt<T> b) { return b + a; }
template <class T> inline constexpr OverflowSafeInt<T> operator - (const int64_t a, const OverflowSafeInt<T> b) { return -b + a; }
template <class T> inline constexpr OverflowSafeInt<T> operator * (const int64_t a, const OverflowSafeInt<T> b) { return b * a; }
template <class T> inline constexpr OverflowSafeInt<T> operator / (const int64_t a, const OverflowSafeInt<T> b) { return (OverflowSafeInt<T>)a / (int)b; }
/* Sometimes we got int operator OverflowSafeInt instead of vice versa. Handle that properly. */
template <class T> inline constexpr OverflowSafeInt<T> operator + (const int a, const OverflowSafeInt<T> b) { return b + a; }
@@ -206,8 +205,8 @@ template <class T> inline constexpr OverflowSafeInt<T> operator - (const byte a
template <class T> inline constexpr OverflowSafeInt<T> operator * (const byte a, const OverflowSafeInt<T> b) { return b * (uint)a; }
template <class T> inline constexpr OverflowSafeInt<T> operator / (const byte a, const OverflowSafeInt<T> b) { return (OverflowSafeInt<T>)a / (int)b; }
typedef OverflowSafeInt<int64> OverflowSafeInt64;
typedef OverflowSafeInt<int32> OverflowSafeInt32;
typedef OverflowSafeInt<int64_t> OverflowSafeInt64;
typedef OverflowSafeInt<int32_t> OverflowSafeInt32;
/* Some basic "unit tests". Also has the bonus of confirming that constexpr is working. */
static_assert(OverflowSafeInt32(INT32_MIN) - 1 == OverflowSafeInt32(INT32_MIN));
@@ -215,6 +214,10 @@ static_assert(OverflowSafeInt32(INT32_MAX) + 1 == OverflowSafeInt32(INT32_MAX));
static_assert(OverflowSafeInt32(INT32_MAX) * 2 == OverflowSafeInt32(INT32_MAX));
static_assert(OverflowSafeInt32(INT32_MIN) * 2 == OverflowSafeInt32(INT32_MIN));
/* Specialisation of the generic ClampTo function for overflow safe integers to normal integers. */
template <typename To, typename From>
constexpr To ClampTo(OverflowSafeInt<From> value) { return ClampTo<To>(From(value)); }
#undef HAS_OVERFLOW_BUILTINS
#endif /* OVERFLOWSAFE_TYPE_HPP */

View File

@@ -20,7 +20,7 @@
{
PoolVector *pools = PoolBase::GetPools();
pools->erase(std::find(pools->begin(), pools->end(), this));
if (pools->size() == 0) delete pools;
if (pools->empty()) delete pools;
}
/**

View File

@@ -13,6 +13,9 @@
#include "alloc_func.hpp"
#include "mem_func.hpp"
#include "pool_type.hpp"
#include "../error_func.h"
#include "../saveload/saveload_error.hpp" // SlErrorCorruptFmt
/**
* Helper for defining the method's signature.
@@ -127,7 +130,7 @@ DEFINE_POOL_METHOD(inline void *)::AllocateItem(size_t size, size_t index)
* Allocates new item
* @param size size of item
* @return pointer to allocated item
* @note error() on failure! (no free item)
* @note FatalError() on failure! (no free item)
*/
DEFINE_POOL_METHOD(void *)::GetNew(size_t size)
{
@@ -138,7 +141,7 @@ DEFINE_POOL_METHOD(void *)::GetNew(size_t size)
this->checked--;
#endif /* WITH_ASSERT */
if (index == NO_FREE_ITEM) {
error("%s: no more free items", this->name);
FatalError("{}: no more free items", this->name);
}
this->first_free = index + 1;
@@ -154,16 +157,14 @@ DEFINE_POOL_METHOD(void *)::GetNew(size_t size)
*/
DEFINE_POOL_METHOD(void *)::GetNew(size_t size, size_t index)
{
extern void NORETURN SlErrorCorruptFmt(const char *format, ...);
if (index >= Tmax_size) {
SlErrorCorruptFmt("%s index " PRINTF_SIZE " out of range (" PRINTF_SIZE ")", this->name, index, Tmax_size);
SlErrorCorruptFmt("{} index {} out of range ({})", this->name, index, Tmax_size);
}
if (index >= this->size) this->ResizeFor(index);
if (this->data[index] != nullptr) {
SlErrorCorruptFmt("%s index " PRINTF_SIZE " already in use", this->name, index);
SlErrorCorruptFmt("{} index {} already in use", this->name, index);
}
return this->AllocateItem(size, index);

View File

@@ -10,7 +10,6 @@
#ifndef POOL_TYPE_HPP
#define POOL_TYPE_HPP
#include "smallvec_type.hpp"
#include "enum_type.hpp"
/** Various types of a pool. */
@@ -80,7 +79,7 @@ private:
template <class Titem, typename Tindex, size_t Tgrowth_step, size_t Tmax_size, PoolType Tpool_type = PT_NORMAL, bool Tcache = false, bool Tzero = true>
struct Pool : PoolBase {
/* Ensure Tmax_size is within the bounds of Tindex. */
static_assert((uint64)(Tmax_size - 1) >> 8 * sizeof(Tindex) == 0);
static_assert((uint64_t)(Tmax_size - 1) >> 8 * sizeof(Tindex) == 0);
static constexpr size_t MAX_SIZE = Tmax_size; ///< Make template parameter accessible from outside
@@ -98,7 +97,7 @@ struct Pool : PoolBase {
Titem **data; ///< Pointer to array of pointers to Titem
Pool(const char *name);
virtual void CleanPool();
void CleanPool() override;
/**
* Returns Titem with given index
@@ -143,8 +142,8 @@ struct Pool : PoolBase {
template <class T>
struct PoolIterator {
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef T *pointer;
typedef T &reference;
typedef size_t difference_type;
typedef std::forward_iterator_tag iterator_category;
@@ -187,8 +186,8 @@ struct Pool : PoolBase {
template <class T, class F>
struct PoolIteratorFiltered {
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef T *pointer;
typedef T &reference;
typedef size_t difference_type;
typedef std::forward_iterator_tag iterator_category;
@@ -276,13 +275,12 @@ struct Pool : PoolBase {
/**
* Allocates space for new Titem at given memory address
* @param size size of Titem
* @param ptr where are we allocating the item?
* @return pointer to allocated memory (== ptr)
* @note use of this is strongly discouraged
* @pre the memory must not be allocated in the Pool!
*/
inline void *operator new(size_t size, void *ptr)
inline void *operator new(size_t, void *ptr)
{
for (size_t i = 0; i < Tpool->first_unused; i++) {
/* Don't allow creating new objects over existing.
@@ -376,7 +374,7 @@ struct Pool : PoolBase {
* @note when this function is called, PoolItem::Get(index) == nullptr.
* @note it's called only when !CleaningPool()
*/
static inline void PostDestructor(size_t index) { }
static inline void PostDestructor([[maybe_unused]] size_t index) { }
/**
* Returns an iterable ensemble of all valid Titem

View File

@@ -10,6 +10,7 @@
#include "../stdafx.h"
#include "random_func.hpp"
#include "bitmath_func.hpp"
#include "../debug.h"
#ifdef RANDOM_DEBUG
#include "../network/network.h"
@@ -17,9 +18,20 @@
#include "../network/network_internal.h"
#include "../company_func.h"
#include "../fileio_func.h"
#include "../date_func.h"
#include "../timer/timer_game_calendar.h"
#endif /* RANDOM_DEBUG */
#if defined(_WIN32)
# include <windows.h>
# include <bcrypt.h>
#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
// No includes required.
#elif defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 25)))
# include <sys/random.h>
#elif defined(__EMSCRIPTEN__)
# include <emscripten.h>
#endif
#include "../safeguards.h"
Randomizer _random, _interactive_random;
@@ -28,13 +40,13 @@ Randomizer _random, _interactive_random;
* Generate the next pseudo random number
* @return the random number
*/
uint32 Randomizer::Next()
uint32_t Randomizer::Next()
{
const uint32 s = this->state[0];
const uint32 t = this->state[1];
const uint32_t s = this->state[0];
const uint32_t t = this->state[1];
this->state[0] = s + ROR(t ^ 0x1234567F, 7) + 1;
return this->state[1] = ROR(s, 3) - 1;
this->state[0] = s + std::rotr(t ^ 0x1234567F, 7) + 1;
return this->state[1] = std::rotr(s, 3) - 1;
}
/**
@@ -43,16 +55,16 @@ uint32 Randomizer::Next()
* @param limit Limit of the range to be generated from.
* @return Random number in [0,\a limit)
*/
uint32 Randomizer::Next(uint32 limit)
uint32_t Randomizer::Next(uint32_t limit)
{
return ((uint64)this->Next() * (uint64)limit) >> 32;
return ((uint64_t)this->Next() * (uint64_t)limit) >> 32;
}
/**
* (Re)set the state of the random number generator.
* @param seed the new state
*/
void Randomizer::SetSeed(uint32 seed)
void Randomizer::SetSeed(uint32_t seed)
{
this->state[0] = seed;
this->state[1] = seed;
@@ -62,24 +74,74 @@ void Randomizer::SetSeed(uint32 seed)
* (Re)set the state of the random number generators.
* @param seed the new state
*/
void SetRandomSeed(uint32 seed)
void SetRandomSeed(uint32_t seed)
{
_random.SetSeed(seed);
_interactive_random.SetSeed(seed * 0x1234567);
}
#ifdef RANDOM_DEBUG
uint32 DoRandom(int line, const char *file)
uint32_t DoRandom(int line, const char *file)
{
if (_networking && (!_network_server || (NetworkClientSocket::IsValidID(0) && NetworkClientSocket::Get(0)->status != NetworkClientSocket::STATUS_INACTIVE))) {
Debug(random, 0, "{:08x}; {:02x}; {:04x}; {:02x}; {}:{}", _date, _date_fract, _frame_counter, (byte)_current_company, file, line);
Debug(random, 0, "{:08x}; {:02x}; {:04x}; {:02x}; {}:{}", TimerGameEconomy::date, TimerGameEconomy::date_fract, _frame_counter, (byte)_current_company, file, line);
}
return _random.Next();
}
uint32 DoRandomRange(uint32 limit, int line, const char *file)
uint32_t DoRandomRange(uint32_t limit, int line, const char *file)
{
return ((uint64)DoRandom(line, file) * (uint64)limit) >> 32;
return ((uint64_t)DoRandom(line, file) * (uint64_t)limit) >> 32;
}
#endif /* RANDOM_DEBUG */
/**
* Fill the given buffer with random bytes.
*
* This function will attempt to use a cryptographically-strong random
* generator, but will fall back to a weaker random generator if none is
* available.
*
* In the end, the buffer will always be filled with some form of random
* bytes when this function returns.
*
* @param buf The buffer to fill with random bytes.
*/
void RandomBytesWithFallback(std::span<uint8_t> buf)
{
#if defined(_WIN32)
auto res = BCryptGenRandom(nullptr, static_cast<PUCHAR>(buf.data()), static_cast<ULONG>(buf.size()), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (res >= 0) return;
#elif defined(__APPLE__) || defined(__NetBSD__) || defined(__FreeBSD__)
arc4random_buf(buf.data(), buf.size());
return;
#elif defined(__GLIBC__) && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 25)))
auto res = getrandom(buf.data(), buf.size(), 0);
if (res > 0 && static_cast<size_t>(res) == buf.size()) return;
#elif defined(__EMSCRIPTEN__)
auto res = EM_ASM_INT({
var buf = $0;
var bytes = $1;
var crypto = window.crypto;
if (crypto === undefined || crypto.getRandomValues === undefined) {
return -1;
}
crypto.getRandomValues(Module.HEAPU8.subarray(buf, buf + bytes));
return 1;
}, buf.data(), buf.size());
if (res > 0) return;
#else
# warning "No cryptographically-strong random generator available; using a fallback instead"
#endif
static bool warned_once = false;
Debug(misc, warned_once ? 1 : 0, "Cryptographically-strong random generator unavailable; using fallback");
warned_once = true;
for (uint i = 0; i < buf.size(); i++) {
buf[i] = static_cast<uint8_t>(InteractiveRandom());
}
}

View File

@@ -20,11 +20,11 @@
*/
struct Randomizer {
/** The state of the randomizer */
uint32 state[2];
uint32_t state[2];
uint32 Next();
uint32 Next(uint32 limit);
void SetSeed(uint32 seed);
uint32_t Next();
uint32_t Next(uint32_t limit);
void SetSeed(uint32_t seed);
};
extern Randomizer _random; ///< Random used in the game state calculations
extern Randomizer _interactive_random; ///< Random used everywhere else, where it does not (directly) influence the game state
@@ -39,7 +39,7 @@ struct SavedRandomSeeds {
* Saves the current seeds
* @param storage Storage for saving
*/
static inline void SaveRandomSeeds(SavedRandomSeeds *storage)
inline void SaveRandomSeeds(SavedRandomSeeds *storage)
{
storage->random = _random;
storage->interactive_random = _interactive_random;
@@ -49,24 +49,24 @@ static inline void SaveRandomSeeds(SavedRandomSeeds *storage)
* Restores previously saved seeds
* @param storage Storage where SaveRandomSeeds() stored th seeds
*/
static inline void RestoreRandomSeeds(const SavedRandomSeeds &storage)
inline void RestoreRandomSeeds(const SavedRandomSeeds &storage)
{
_random = storage.random;
_interactive_random = storage.interactive_random;
}
void SetRandomSeed(uint32 seed);
void SetRandomSeed(uint32_t seed);
#ifdef RANDOM_DEBUG
# ifdef __APPLE__
# define OTTD_Random() DoRandom(__LINE__, __FILE__)
# else
# define Random() DoRandom(__LINE__, __FILE__)
# endif
uint32 DoRandom(int line, const char *file);
uint32_t DoRandom(int line, const char *file);
# define RandomRange(limit) DoRandomRange(limit, __LINE__, __FILE__)
uint32 DoRandomRange(uint32 limit, int line, const char *file);
uint32_t DoRandomRange(uint32_t limit, int line, const char *file);
#else
static inline uint32 Random()
static inline uint32_t Random()
{
return _random.Next();
}
@@ -78,18 +78,18 @@ void SetRandomSeed(uint32 seed);
* @param limit Limit for the range to be picked from.
* @return A random number in [0,\a limit).
*/
static inline uint32 RandomRange(uint32 limit)
static inline uint32_t RandomRange(uint32_t limit)
{
return _random.Next(limit);
}
#endif
static inline uint32 InteractiveRandom()
inline uint32_t InteractiveRandom()
{
return _interactive_random.Next();
}
static inline uint32 InteractiveRandomRange(uint32 limit)
inline uint32_t InteractiveRandomRange(uint32_t limit)
{
return _interactive_random.Next(limit);
}
@@ -109,10 +109,10 @@ static inline uint32 InteractiveRandomRange(uint32 limit)
* @param r The given randomize-number
* @return True if the probability given by r is less or equal to (a/b)
*/
static inline bool Chance16I(const uint a, const uint b, const uint32 r)
inline bool Chance16I(const uint a, const uint b, const uint32_t r)
{
assert(b != 0);
return (((uint16)r * b + b / 2) >> 16) < a;
return (((uint16_t)r * b + b / 2) >> 16) < a;
}
/**
@@ -128,7 +128,7 @@ static inline bool Chance16I(const uint a, const uint b, const uint32 r)
#ifdef RANDOM_DEBUG
# define Chance16(a, b) Chance16I(a, b, DoRandom(__LINE__, __FILE__))
#else
static inline bool Chance16(const uint a, const uint b)
inline bool Chance16(const uint a, const uint b)
{
return Chance16I(a, b, Random());
}
@@ -152,11 +152,13 @@ static inline bool Chance16(const uint a, const uint b)
#ifdef RANDOM_DEBUG
# define Chance16R(a, b, r) (r = DoRandom(__LINE__, __FILE__), Chance16I(a, b, r))
#else
static inline bool Chance16R(const uint a, const uint b, uint32 &r)
inline bool Chance16R(const uint a, const uint b, uint32_t &r)
{
r = Random();
return Chance16I(a, b, r);
}
#endif /* RANDOM_DEBUG */
void RandomBytesWithFallback(std::span<uint8_t> buf);
#endif /* RANDOM_FUNC_HPP */

View File

@@ -1,151 +0,0 @@
/*
* 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 smallmap_type.hpp Simple mapping class targeted for small sets of data. Stored data shall be POD ("Plain Old Data")! */
#ifndef SMALLMAP_TYPE_HPP
#define SMALLMAP_TYPE_HPP
#include "smallvec_type.hpp"
#include <utility>
/**
* Implementation of simple mapping class.
* It has inherited accessors from std::vector().
* @tparam T Key type.
* @tparam U Value type.
* @tparam S Unit of allocation.
*
* @see std::vector
*/
template <typename T, typename U>
struct SmallMap : std::vector<std::pair<T, U> > {
typedef std::pair<T, U> Pair;
typedef Pair *iterator;
typedef const Pair *const_iterator;
/** Creates new SmallMap. Data are initialized in std::vector constructor */
inline SmallMap() { }
/** Data are freed in std::vector destructor */
inline ~SmallMap() { }
/**
* Finds given key in this map
* @param key key to find
* @return &Pair(key, data) if found, this->End() if not
*/
inline typename std::vector<Pair>::const_iterator Find(const T &key) const
{
typename std::vector<Pair>::const_iterator it;
for (it = std::vector<Pair>::begin(); it != std::vector<Pair>::end(); it++) {
if (key == it->first) return it;
}
return it;
}
/**
* Finds given key in this map
* @param key key to find
* @return &Pair(key, data) if found, this->End() if not
*/
inline Pair *Find(const T &key)
{
for (uint i = 0; i < std::vector<Pair>::size(); i++) {
if (key == std::vector<Pair>::operator[](i).first) return &std::vector<Pair>::operator[](i);
}
return this->End();
}
inline const Pair *End() const
{
return std::vector<Pair>::data() + std::vector<Pair>::size();
}
inline Pair *End()
{
return std::vector<Pair>::data() + std::vector<Pair>::size();
}
/**
* Tests whether a key is assigned in this map.
* @param key key to test
* @return true iff the item is present
*/
inline bool Contains(const T &key) const
{
return this->Find(key) != std::vector<Pair>::end();
}
/**
* Tests whether a key is assigned in this map.
* @param key key to test
* @return true iff the item is present
*/
inline bool Contains(const T &key)
{
return this->Find(key) != this->End();
}
/**
* Removes given pair from this map
* @param pair pair to remove
* @note it has to be pointer to pair in this map. It is overwritten by the last item.
*/
inline void Erase(Pair *pair)
{
assert(pair >= std::vector<Pair>::data() && pair < this->End());
auto distance = pair - std::vector<Pair>::data();
std::vector<Pair>::erase(std::vector<Pair>::begin() + distance);
}
/**
* Removes given key from this map
* @param key key to remove
* @return true iff the key was found
* @note last item is moved to its place, so don't increase your iterator if true is returned!
*/
inline bool Erase(const T &key)
{
auto *pair = this->Find(key);
if (pair == this->End()) return false;
this->Erase(pair);
return true;
}
/**
* Adds new item to this map.
* @param key key
* @param data data
* @return true iff the key wasn't already present
*/
inline bool Insert(const T &key, const U &data)
{
if (this->Contains(key)) return false;
std::vector<Pair>::emplace_back(key, data);
return true;
}
/**
* Returns data belonging to this key
* @param key key
* @return data belonging to this key
* @note if this key wasn't present, new entry is created
*/
inline U &operator[](const T &key)
{
for (uint i = 0; i < std::vector<Pair>::size(); i++) {
if (key == std::vector<Pair>::operator[](i).first) return std::vector<Pair>::operator[](i).second;
}
Pair &n = std::vector<Pair>::emplace_back();
n.first = key;
return n.second;
}
};
#endif /* SMALLMAP_TYPE_HPP */

View File

@@ -1,320 +0,0 @@
/*
* 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 smallmatrix_type.hpp Simple matrix class that allows allocating an item without the need to copy this->data needlessly. */
#ifndef SMALLMATRIX_TYPE_HPP
#define SMALLMATRIX_TYPE_HPP
#include "alloc_func.hpp"
#include "mem_func.hpp"
/**
* Simple matrix template class.
*
* Allocating a matrix in one piece reduces overhead in allocations compared
* with allocating a vector of vectors and saves some pointer dereferencing.
* However, you can only get rectangular matrixes like this and if you're
* changing their height very often performance will probably be worse than
* with a vector of vectors, due to more frequent copying of memory blocks.
*
* No iterators are provided as iterating the columns would require persistent
* column objects. Those do not exist. Providing iterators with transient
* column objects would tie each iterator to a column object, thus replacing
* previously retrieved columns when iterating and defeating the point of
* iteration.
*
* It's expected that the items don't need to be constructed or deleted by the
* container. Only memory allocation and deallocation is performed. This is the
* same for all openttd "SmallContainer" classes.
*
* @tparam T The type of the items stored
*/
template <typename T>
class SmallMatrix {
protected:
T *data; ///< The pointer to the first item
uint width; ///< Number of items over first axis
uint height; ///< Number of items over second axis
uint capacity; ///< The available space for storing items
public:
SmallMatrix() : data(nullptr), width(0), height(0), capacity(0) {}
/**
* Copy constructor.
* @param other The other matrix to copy.
*/
SmallMatrix(const SmallMatrix &other) : data(nullptr), width(0), height(0), capacity(0)
{
this->Assign(other);
}
~SmallMatrix()
{
free(this->data);
}
/**
* Assignment.
* @param other The other matrix to assign.
*/
SmallMatrix &operator=(const SmallMatrix &other)
{
this->Assign(other);
return *this;
}
/**
* Assign items from other vector.
*/
inline void Assign(const SmallMatrix &other)
{
if (&other == this) return;
this->height = other.Height();
this->width = other.Width();
uint num_items = this->width * this->height;
if (num_items > this->capacity) {
this->capacity = num_items;
free(this->data);
this->data = MallocT<T>(num_items);
MemCpyT(this->data, other[0], num_items);
} else if (num_items > 0) {
MemCpyT(this->data, other[0], num_items);
}
}
/**
* Remove all rows from the matrix.
*/
inline void Clear()
{
/* In fact we just reset the width avoiding the need to
* probably reallocate the same amount of memory the matrix was
* previously using. */
this->width = 0;
}
/**
* Remove all items from the list and free allocated memory.
*/
inline void Reset()
{
this->height = 0;
this->width = 0;
this->capacity = 0;
free(this->data);
this->data = nullptr;
}
/**
* Compact the matrix down to the smallest possible size.
*/
inline void Compact()
{
uint capacity = this->height * this->width;
if (capacity >= this->capacity) return;
this->capacity = capacity;
this->data = ReallocT(this->data, this->capacity);
}
/**
* Erase a column, replacing it with the last one.
* @param x Position of the column.
*/
void EraseColumn(uint x)
{
if (x < --this->width) {
MemCpyT<T>(this->data + x * this->height,
this->data + this->width * this->height,
this->height);
}
}
/**
* Remove columns from the matrix while preserving the order of other columns.
* @param x First column to remove.
* @param count Number of consecutive columns to remove.
*/
void EraseColumnPreservingOrder(uint x, uint count = 1)
{
if (count == 0) return;
assert(x < this->width);
assert(x + count <= this->width);
this->width -= count;
uint to_move = (this->width - x) * this->height;
if (to_move > 0) {
MemMoveT(this->data + x * this->height,
this->data + (x + count) * this->height, to_move);
}
}
/**
* Erase a row, replacing it with the last one.
* @param y Position of the row.
*/
void EraseRow(uint y)
{
if (y < this->height - 1) {
for (uint x = 0; x < this->width; ++x) {
this->data[x * this->height + y] =
this->data[(x + 1) * this->height - 1];
}
}
this->Resize(this->width, this->height - 1);
}
/**
* Remove columns from the matrix while preserving the order of other columns.
* @param y First column to remove.
* @param count Number of consecutive columns to remove.
*/
void EraseRowPreservingOrder(uint y, uint count = 1)
{
if (this->height > count + y) {
for (uint x = 0; x < this->width; ++x) {
MemMoveT(this->data + x * this->height + y,
this->data + x * this->height + y + count,
this->height - count - y);
}
}
this->Resize(this->width, this->height - count);
}
/**
* Append rows.
* @param to_add Number of rows to append.
*/
inline void AppendRow(uint to_add = 1)
{
this->Resize(this->width, to_add + this->height);
}
/**
* Append rows.
* @param to_add Number of rows to append.
*/
inline void AppendColumn(uint to_add = 1)
{
this->Resize(to_add + this->width, this->height);
}
/**
* Set the size to a specific width and height, preserving item positions
* as far as possible in the process.
* @param new_width Target width.
* @param new_height Target height.
*/
inline void Resize(uint new_width, uint new_height)
{
uint new_capacity = new_width * new_height;
T *new_data = nullptr;
void (*copy)(T *dest, const T *src, size_t count) = nullptr;
if (new_capacity > this->capacity) {
/* If the data doesn't fit into current capacity, resize and copy ... */
new_data = MallocT<T>(new_capacity);
copy = &MemCpyT<T>;
} else {
/* ... otherwise just move the columns around, if necessary. */
new_data = this->data;
copy = &MemMoveT<T>;
}
if (this->height != new_height || new_data != this->data) {
if (this->height > 0) {
if (new_height > this->height) {
/* If matrix is growing, copy from the back to avoid
* overwriting uncopied data. */
for (uint x = this->width; x > 0; --x) {
if (x * new_height > new_capacity) continue;
(*copy)(new_data + (x - 1) * new_height,
this->data + (x - 1) * this->height,
std::min(this->height, new_height));
}
} else {
/* If matrix is shrinking copy from the front. */
for (uint x = 0; x < this->width; ++x) {
if ((x + 1) * new_height > new_capacity) break;
(*copy)(new_data + x * new_height,
this->data + x * this->height,
std::min(this->height, new_height));
}
}
}
this->height = new_height;
if (new_data != this->data) {
free(this->data);
this->data = new_data;
this->capacity = new_capacity;
}
}
this->width = new_width;
}
inline uint Height() const
{
return this->height;
}
inline uint Width() const
{
return this->width;
}
/**
* Get item x/y (const).
*
* @param x X-position of the item.
* @param y Y-position of the item.
* @return Item at specified position.
*/
inline const T &Get(uint x, uint y) const
{
assert(x < this->width && y < this->height);
return this->data[x * this->height + y];
}
/**
* Get item x/y.
*
* @param x X-position of the item.
* @param y Y-position of the item.
* @return Item at specified position.
*/
inline T &Get(uint x, uint y)
{
assert(x < this->width && y < this->height);
return this->data[x * this->height + y];
}
/**
* Get column "number" (const)
*
* @param x Position of the column.
* @return Column at "number".
*/
inline const T *operator[](uint x) const
{
assert(x < this->width);
return this->data + x * this->height;
}
/**
* Get column "number" (const)
*
* @param x Position of the column.
* @return Column at "number".
*/
inline T *operator[](uint x)
{
assert(x < this->width);
return this->data + x * this->height;
}
};
#endif /* SMALLMATRIX_TYPE_HPP */

View File

@@ -10,7 +10,6 @@
#ifndef SMALLSTACK_TYPE_HPP
#define SMALLSTACK_TYPE_HPP
#include "smallvec_type.hpp"
#include <mutex>
/**

View File

@@ -1,102 +0,0 @@
/*
* 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 span_type.hpp Minimized implementation of C++20 std::span. */
#ifndef CORE_SPAN_TYPE_HPP
#define CORE_SPAN_TYPE_HPP
/* This is a partial copy/paste from https://github.com/gsl-lite/gsl-lite/blob/master/include/gsl/gsl-lite.hpp */
/* Template to check if a template variable defines size() and data(). */
template <class, class = void>
struct has_size_and_data : std::false_type{};
template <class C>
struct has_size_and_data
<
C, std::void_t<
decltype(std::size(std::declval<C>())),
decltype(std::data(std::declval<C>()))>
> : std::true_type{};
/* Template to check if two elements are compatible. */
template <class, class, class = void>
struct is_compatible_element : std::false_type {};
template <class C, class E>
struct is_compatible_element
<
C, E, std::void_t<
decltype(std::data(std::declval<C>())),
typename std::remove_pointer_t<decltype(std::data( std::declval<C&>()))>(*)[]>
> : std::is_convertible<typename std::remove_pointer_t<decltype(std::data(std::declval<C&>()))>(*)[], E(*)[]>{};
/* Template to check if a container is compatible. gsl-lite also includes is_array and is_std_array, but as we don't use them, they are omitted. */
template <class C, class E>
struct is_compatible_container : std::bool_constant
<
has_size_and_data<C>::value
&& is_compatible_element<C,E>::value
>{};
/**
* A trimmed down version of what std::span will be in C++20.
*
* It is fully forwards compatible, so if this codebase switches to C++20,
* all "span" instances can be replaced by "std::span" without loss of
* functionality.
*
* Currently it only supports basic functionality:
* - size() and friends
* - begin() and friends
*
* It is meant to simplify function parameters, where we only want to walk
* a continuous list.
*/
template<class T>
class span {
public:
typedef T element_type;
typedef typename std::remove_cv< T >::type value_type;
typedef T &reference;
typedef T *pointer;
typedef const T &const_reference;
typedef const T *const_pointer;
typedef pointer iterator;
typedef const_pointer const_iterator;
typedef size_t size_type;
typedef std::ptrdiff_t difference_type;
constexpr span() noexcept : first(nullptr), last(nullptr) {}
constexpr span(pointer data_in, size_t size_in) : first(data_in), last(data_in + size_in) {}
template<class Container, typename std::enable_if<(is_compatible_container<Container, element_type>::value), int>::type = 0>
constexpr span(Container &list) noexcept : first(std::data(list)), last(std::data(list) + std::size(list)) {}
template<class Container, typename std::enable_if<(std::is_const<element_type>::value && is_compatible_container<Container, element_type>::value), int>::type = 0>
constexpr span(const Container &list) noexcept : first(std::data(list)), last(std::data(list) + std::size(list)) {}
constexpr size_t size() const noexcept { return static_cast<size_t>( last - first ); }
constexpr std::ptrdiff_t ssize() const noexcept { return static_cast<std::ptrdiff_t>( last - first ); }
constexpr bool empty() const noexcept { return size() == 0; }
constexpr iterator begin() const noexcept { return iterator(first); }
constexpr iterator end() const noexcept { return iterator(last); }
constexpr const_iterator cbegin() const noexcept { return const_iterator(first); }
constexpr const_iterator cend() const noexcept { return const_iterator(last); }
constexpr reference operator[](size_type idx) const { return first[idx]; }
private:
pointer first;
pointer last;
};
#endif /* CORE_SPAN_TYPE_HPP */

View File

@@ -1,27 +0,0 @@
/*
* 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 string_compare_type.hpp Comparator class for "const char *" so it can be used as a key for std::map */
#ifndef STRING_COMPARE_TYPE_HPP
#define STRING_COMPARE_TYPE_HPP
/** Comparator for strings. */
struct StringCompare {
/**
* Compare two strings.
* @param a The first string.
* @param b The second string.
* @return True is the first string is deemed "lower" than the second string.
*/
bool operator () (const char *a, const char *b) const
{
return strcmp(a, b) < 0;
}
};
#endif /* STRING_COMPARE_TYPE_HPP */

View File

@@ -10,64 +10,173 @@
#ifndef STRONG_TYPEDEF_TYPE_HPP
#define STRONG_TYPEDEF_TYPE_HPP
/** Non-templated base for #StrongTypedef for use with type trait queries. */
#include "../3rdparty/fmt/format.h"
/** Non-templated base for #StrongType::Typedef 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;
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; }
T value{}; ///< Backing storage field.
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; }
constexpr StrongTypedef() = default;
constexpr StrongTypedef(const StrongTypedef &o) = default;
constexpr StrongTypedef(StrongTypedef &&o) = default;
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; }
constexpr StrongTypedef(const T &value) : value(value) {}
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; }
constexpr Tthis &operator =(const StrongTypedef &rhs) { this->value = rhs.value; return static_cast<Tthis &>(*this); }
constexpr Tthis &operator =(StrongTypedef &&rhs) { this->value = std::move(rhs.value); return static_cast<Tthis &>(*this); }
constexpr Tthis &operator =(const T &rhs) { this->value = rhs; return static_cast<Tthis &>(*this); }
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; }
explicit constexpr operator T() const { return this->value; }
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; }
};
};
constexpr bool operator ==(const StrongTypedef &rhs) const { return this->value == rhs.value; }
constexpr bool operator !=(const StrongTypedef &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; }
};
/**
* 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; }
/**
* 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;
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 }; }
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; }
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 }; }
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); }
/* 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 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 }; }
};
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 */