This commit is contained in:
Jordan Woyak 2025-05-27 18:37:58 -05:00 committed by GitHub
commit 59a7c75c1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 34 additions and 55 deletions

View File

@ -114,21 +114,17 @@
*/ */
#pragma pack(1) #pragma pack(1)
template <std::size_t position, std::size_t bits, typename T, template <std::size_t position, std::size_t bits, typename T,
// StorageType is T for non-enum types and the underlying type of T if // StorageType is T for non-enum or the underlying-type for an enum.
// T is an enumeration. Note that T is wrapped within an enable_if in the typename StorageType = std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
// former case to workaround compile errors which arise when using std::type_identity<T>>::type>
// std::underlying_type<T>::type directly.
typename StorageType = typename std::conditional_t<
std::is_enum<T>::value, std::underlying_type<T>, std::enable_if<true, T>>::type>
struct BitField struct BitField
{ {
private: public:
// This constructor might be considered ambiguous: // This constructor might be considered ambiguous:
// Would it initialize the storage or just the bitfield? // Would it initialize the storage or just the bitfield?
// Hence, delete it. Use the assignment operator to set bitfield values! // Hence, delete it. Use the assignment operator to set bitfield values!
BitField(T val) = delete; BitField(T val) = delete;
public:
// Force default constructor to be created // Force default constructor to be created
// so that we can use this within unions // so that we can use this within unions
constexpr BitField() = default; constexpr BitField() = default;
@ -212,26 +208,22 @@ class BitFieldArrayIterator;
#pragma pack(1) #pragma pack(1)
template <std::size_t position, std::size_t bits, std::size_t size, typename T, template <std::size_t position, std::size_t bits, std::size_t size, typename T,
// StorageType is T for non-enum types and the underlying type of T if // StorageType is T for non-enum or the underlying-type for an enum.
// T is an enumeration. Note that T is wrapped within an enable_if in the typename StorageType = std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>,
// former case to workaround compile errors which arise when using std::type_identity<T>>::type>
// std::underlying_type<T>::type directly.
typename StorageType = typename std::conditional_t<
std::is_enum<T>::value, std::underlying_type<T>, std::enable_if<true, T>>::type>
struct BitFieldArray struct BitFieldArray
{ {
public:
using Ref = BitFieldArrayRef<position, bits, size, T, StorageType>; using Ref = BitFieldArrayRef<position, bits, size, T, StorageType>;
using ConstRef = BitFieldArrayConstRef<position, bits, size, T, StorageType>; using ConstRef = BitFieldArrayConstRef<position, bits, size, T, StorageType>;
using Iterator = BitFieldArrayIterator<position, bits, size, T, StorageType>; using Iterator = BitFieldArrayIterator<position, bits, size, T, StorageType>;
using ConstIterator = BitFieldArrayConstIterator<position, bits, size, T, StorageType>; using ConstIterator = BitFieldArrayConstIterator<position, bits, size, T, StorageType>;
private:
// This constructor might be considered ambiguous: // This constructor might be considered ambiguous:
// Would it initialize the storage or just the bitfield? // Would it initialize the storage or just the bitfield?
// Hence, delete it. Use the assignment operator to set bitfield values! // Hence, delete it. Use the assignment operator to set bitfield values!
BitFieldArray(T val) = delete; BitFieldArray(T val) = delete;
public:
// Force default constructor to be created // Force default constructor to be created
// so that we can use this within unions // so that we can use this within unions
constexpr BitFieldArray() = default; constexpr BitFieldArray() = default;
@ -244,7 +236,6 @@ public:
// code expects that this class is trivially copyable. // code expects that this class is trivially copyable.
BitFieldArray& operator=(const BitFieldArray&) = delete; BitFieldArray& operator=(const BitFieldArray&) = delete;
public:
constexpr bool IsSigned() const { return std::is_signed<T>(); } constexpr bool IsSigned() const { return std::is_signed<T>(); }
constexpr std::size_t StartBit() const { return position; } constexpr std::size_t StartBit() const { return position; }
constexpr std::size_t NumBits() const { return bits; } constexpr std::size_t NumBits() const { return bits; }

View File

@ -7,7 +7,6 @@
#include <bit> #include <bit>
#include <climits> #include <climits>
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <cstring> #include <cstring>
#include <initializer_list> #include <initializer_list>
#include <type_traits> #include <type_traits>
@ -117,8 +116,8 @@ constexpr Result ExtractBits(const T src) noexcept
template <typename T> template <typename T>
constexpr bool IsValidLowMask(const T mask) noexcept constexpr bool IsValidLowMask(const T mask) noexcept
{ {
static_assert(std::is_integral<T>::value, "Mask must be an integral type."); static_assert(std::is_integral_v<T>, "Mask must be an integral type.");
static_assert(std::is_unsigned<T>::value, "Signed masks can introduce hard to find bugs."); static_assert(std::is_unsigned_v<T>, "Signed masks can introduce hard to find bugs.");
// Can be efficiently determined without looping or bit counting. It's the counterpart // Can be efficiently determined without looping or bit counting. It's the counterpart
// to https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 // to https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
@ -138,11 +137,10 @@ public:
explicit BitCastPtrType(PtrType* ptr) : m_ptr(ptr) {} explicit BitCastPtrType(PtrType* ptr) : m_ptr(ptr) {}
// Enable operator= only for pointers to non-const data // Enable operator= only for pointers to non-const data
template <typename S> auto& operator=(const T& source) requires(!std::is_const_v<PtrType>)
inline typename std::enable_if<std::is_same<S, T>() && !std::is_const<PtrType>()>::type
operator=(const S& source)
{ {
std::memcpy(m_ptr, &source, sizeof(source)); std::memcpy(m_ptr, &source, sizeof(source));
return *this;
} }
inline operator T() const inline operator T() const

View File

@ -32,7 +32,6 @@
#include "Common/EnumMap.h" #include "Common/EnumMap.h"
#include "Common/Flag.h" #include "Common/Flag.h"
#include "Common/Inline.h" #include "Common/Inline.h"
#include "Common/Logging/Log.h"
// Wrapper class // Wrapper class
class PointerWrap class PointerWrap
@ -195,13 +194,15 @@ public:
DoArray(x.data(), static_cast<u32>(x.size())); DoArray(x.data(), static_cast<u32>(x.size()));
} }
template <typename T, typename std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0> template <typename T>
requires(std::is_trivially_copyable_v<T>)
void DoArray(T* x, u32 count) void DoArray(T* x, u32 count)
{ {
DoVoid(x, count * sizeof(T)); DoVoid(x, count * sizeof(T));
} }
template <typename T, typename std::enable_if_t<!std::is_trivially_copyable_v<T>, int> = 0> template <typename T>
requires(!std::is_trivially_copyable_v<T>)
void DoArray(T* x, u32 count) void DoArray(T* x, u32 count)
{ {
for (u32 i = 0; i < count; ++i) for (u32 i = 0; i < count; ++i)

View File

@ -6,21 +6,14 @@
#include <mutex> #include <mutex>
#include <shared_mutex> #include <shared_mutex>
#include <string> #include <string>
#include <type_traits>
#include <utility> #include <utility>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Config/Enums.h" #include "Common/Config/Enums.h"
#include "Common/TypeUtils.h"
namespace Config namespace Config
{ {
namespace detail
{
// std::underlying_type may only be used with enum types, so make sure T is an enum type first.
template <typename T>
using UnderlyingType = typename std::enable_if_t<std::is_enum<T>{}, std::underlying_type<T>>::type;
} // namespace detail
struct Location struct Location
{ {
System system{}; System system{};
@ -54,8 +47,7 @@ public:
// Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>> // Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>>
// so that enum settings can still easily work with code that doesn't care about the enum values. // so that enum settings can still easily work with code that doesn't care about the enum values.
template <typename Enum, template <Common::TypedEnum<T> Enum>
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
Info(const Info<Enum>& other) Info(const Info<Enum>& other)
{ {
*this = other; *this = other;
@ -80,8 +72,7 @@ public:
// Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>> // Make it easy to convert Info<Enum> into Info<UnderlyingType<Enum>>
// so that enum settings can still easily work with code that doesn't care about the enum values. // so that enum settings can still easily work with code that doesn't care about the enum values.
template <typename Enum, template <Common::TypedEnum<T> Enum>
std::enable_if_t<std::is_same<T, detail::UnderlyingType<Enum>>::value>* = nullptr>
Info<T>& operator=(const Info<Enum>& other) Info<T>& operator=(const Info<Enum>& other)
{ {
m_location = other.GetLocation(); m_location = other.GetLocation();

View File

@ -23,7 +23,8 @@ struct DefaultState
namespace detail namespace detail
{ {
template <typename T, std::enable_if_t<!std::is_enum<T>::value>* = nullptr> template <typename T>
requires(!Common::Enum<T>)
std::optional<T> TryParse(const std::string& str_value) std::optional<T> TryParse(const std::string& str_value)
{ {
T value; T value;
@ -32,7 +33,7 @@ std::optional<T> TryParse(const std::string& str_value)
return value; return value;
} }
template <typename T, std::enable_if_t<std::is_enum<T>::value>* = nullptr> template <Common::Enum T>
std::optional<T> TryParse(const std::string& str_value) std::optional<T> TryParse(const std::string& str_value)
{ {
const auto result = TryParse<std::underlying_type_t<T>>(str_value); const auto result = TryParse<std::underlying_type_t<T>>(str_value);

View File

@ -42,11 +42,10 @@
* constexpr formatter() : EnumFormatter(names) {} * constexpr formatter() : EnumFormatter(names) {}
* }; * };
*/ */
template <auto last_member> template <Common::Enum auto last_member>
class EnumFormatter class EnumFormatter
{ {
using T = decltype(last_member); using T = decltype(last_member);
static_assert(std::is_enum_v<T>);
public: public:
constexpr auto parse(fmt::format_parse_context& ctx) constexpr auto parse(fmt::format_parse_context& ctx)

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include <type_traits>
#include "Common/TypeUtils.h" #include "Common/TypeUtils.h"
@ -15,11 +14,10 @@ namespace Common
{ {
// A type that allows lookup of values associated with an enum as the key. // A type that allows lookup of values associated with an enum as the key.
// Designed for enums whose numeric values start at 0 and increment continuously with few gaps. // Designed for enums whose numeric values start at 0 and increment continuously with few gaps.
template <typename V, auto last_member> template <typename V, Common::Enum auto last_member>
class EnumMap final class EnumMap final
{ {
using T = decltype(last_member); using T = decltype(last_member);
static_assert(std::is_enum_v<T>);
static constexpr size_t s_size = static_cast<size_t>(last_member) + 1; static constexpr size_t s_size = static_cast<size_t>(last_member) + 1;
using array_type = std::array<V, s_size>; using array_type = std::array<V, s_size>;
@ -34,8 +32,9 @@ public:
constexpr EnumMap& operator=(EnumMap&& other) = default; constexpr EnumMap& operator=(EnumMap&& other) = default;
// Constructor that accepts exactly size Vs (enforcing that all must be specified). // Constructor that accepts exactly size Vs (enforcing that all must be specified).
template <typename... T, typename = std::enable_if_t<Common::IsNOf<V, s_size, T...>::value>> template <typename... T>
constexpr EnumMap(T... values) : m_array{static_cast<V>(values)...} constexpr EnumMap(T... values) requires(Common::IsNOf<V, s_size, T...>::value)
: m_array{static_cast<V>(values)...}
{ {
} }

View File

@ -3,26 +3,25 @@
#pragma once #pragma once
#include <type_traits>
#include <SFML/Network/Packet.hpp> #include <SFML/Network/Packet.hpp>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/EnumUtils.h"
#include "Common/Swap.h" #include "Common/Swap.h"
#include "Common/TypeUtils.h"
sf::Packet& operator>>(sf::Packet& packet, Common::BigEndianValue<u16>& data); sf::Packet& operator>>(sf::Packet& packet, Common::BigEndianValue<u16>& data);
sf::Packet& operator>>(sf::Packet& packet, Common::BigEndianValue<u32>& data); sf::Packet& operator>>(sf::Packet& packet, Common::BigEndianValue<u32>& data);
sf::Packet& operator>>(sf::Packet& packet, Common::BigEndianValue<u64>& data); sf::Packet& operator>>(sf::Packet& packet, Common::BigEndianValue<u64>& data);
template <typename Enum, std::enable_if_t<std::is_enum_v<Enum>>* = nullptr> template <Common::Enum Enum>
sf::Packet& operator<<(sf::Packet& packet, Enum e) sf::Packet& operator<<(sf::Packet& packet, Enum e)
{ {
using Underlying = std::underlying_type_t<Enum>; packet << Common::ToUnderlying(e);
packet << static_cast<Underlying>(e);
return packet; return packet;
} }
template <typename Enum, std::enable_if_t<std::is_enum_v<Enum>>* = nullptr> template <Common::Enum Enum>
sf::Packet& operator>>(sf::Packet& packet, Enum& e) sf::Packet& operator>>(sf::Packet& packet, Enum& e)
{ {
using Underlying = std::underlying_type_t<Enum>; using Underlying = std::underlying_type_t<Enum>;

View File

@ -70,8 +70,8 @@ static_assert(!std::is_same_v<ObjectType<&Bar::c>, Bar>);
// Template for checking if Types is count occurrences of T. // Template for checking if Types is count occurrences of T.
template <typename T, size_t count, typename... Ts> template <typename T, size_t count, typename... Ts>
struct IsNOf : std::integral_constant<bool, std::conjunction_v<std::is_convertible<Ts, T>...> && struct IsNOf : std::bool_constant<std::conjunction_v<std::is_convertible<Ts, T>...> &&
sizeof...(Ts) == count> sizeof...(Ts) == count>
{ {
}; };