From 12e55ae31b7bc9ebab3b4ebc8d8b07ca4d1373d1 Mon Sep 17 00:00:00 2001 From: FearlessTobi Date: Thu, 22 Feb 2024 16:26:54 +0100 Subject: [PATCH] common: Add various utility classes --- src/common/CMakeLists.txt | 2 + src/common/enum_util.h | 20 ++++++++ src/common/parent_of_member.h | 20 +------- src/common/swap.h | 42 +++++++++++++++ src/common/typed_storage.h | 96 +++++++++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 src/common/enum_util.h create mode 100644 src/common/typed_storage.h diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 779be211e6..abb54c4c7b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -46,6 +46,7 @@ add_library(common STATIC dynamic_library.cpp dynamic_library.h elf.h + enum_util.h error.cpp error.h expected.h @@ -148,6 +149,7 @@ add_library(common STATIC tiny_mt.h tree.h typed_address.h + typed_storage.h uint128.h unique_function.h uuid.cpp diff --git a/src/common/enum_util.h b/src/common/enum_util.h new file mode 100644 index 0000000000..b7f3abf28d --- /dev/null +++ b/src/common/enum_util.h @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace Common { + +template + requires std::is_enum::value +constexpr typename std::underlying_type::type ToUnderlying(Enum e) { + return static_cast::type>(e); +} + +template + requires std::is_enum::value +constexpr Enum FromUnderlying(typename std::underlying_type::type v) { + return static_cast(v); +} + +} // namespace Common diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h index 8e03f17d8b..c9537d2ca3 100644 --- a/src/common/parent_of_member.h +++ b/src/common/parent_of_member.h @@ -6,27 +6,9 @@ #include #include "common/assert.h" +#include "common/typed_storage.h" namespace Common { -namespace detail { -template -struct TypedStorageImpl { - alignas(Align) u8 storage_[Size]; -}; -} // namespace detail - -template -using TypedStorage = detail::TypedStorageImpl; - -template -static constexpr T* GetPointer(TypedStorage& ts) { - return static_cast(static_cast(std::addressof(ts.storage_))); -} - -template -static constexpr const T* GetPointer(const TypedStorage& ts) { - return static_cast(static_cast(std::addressof(ts.storage_))); -} namespace impl { diff --git a/src/common/swap.h b/src/common/swap.h index fde343e452..dea833d6c4 100644 --- a/src/common/swap.h +++ b/src/common/swap.h @@ -84,6 +84,48 @@ namespace Common { return f; } +consteval bool IsLittleEndian() { + return std::endian::native == std::endian::little; +} + +consteval bool IsBigEndian() { + return std::endian::native == std::endian::big; +} +static_assert(IsLittleEndian() ^ IsBigEndian()); + +template +U SwapEndian(const U u) { + constexpr std::size_t BitSizeU8 = 8; + if constexpr (sizeof(U) * BitSizeU8 == 64) { + return swap64(u); + } else if constexpr (sizeof(U) * BitSizeU8 == 32) { + return swap32(u); + } else if constexpr (sizeof(U) * BitSizeU8 == 16) { + return swap16(u); + } else if constexpr (sizeof(U) * BitSizeU8 == 8) { + return u; + } else { + static_assert(!std::is_same::value); + } +} + +template +constexpr T ConvertToLittleEndian(const T val) { + using U = typename std::make_unsigned::type; + + if constexpr (IsBigEndian()) { + return static_cast(SwapEndian(static_cast(val))); + } else { + static_assert(IsLittleEndian()); + return static_cast(static_cast(val)); + } +} + +template +constexpr T LoadLittleEndian(const T* ptr) { + return ConvertToLittleEndian(*ptr); +} + } // Namespace Common template diff --git a/src/common/typed_storage.h b/src/common/typed_storage.h new file mode 100644 index 0000000000..e3ede3d15d --- /dev/null +++ b/src/common/typed_storage.h @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include "common/common_funcs.h" + +namespace Common { + +template +struct TypedStorage { + typename std::aligned_storage::type _storage; +}; + +template +static T* GetPointer(TypedStorage& ts) { + return std::launder(reinterpret_cast(std::addressof(ts._storage))); +} + +template +static const T* GetPointer(const TypedStorage& ts) { + return std::launder(reinterpret_cast(std::addressof(ts._storage))); +} + +template +static T& GetReference(TypedStorage& ts) { + return *GetPointer(ts); +} + +template +static const T& GetReference(const TypedStorage& ts) { + return *GetPointer(ts); +} + +namespace Impl { + +template +static T* GetPointerForConstructAt(TypedStorage& ts) { + return reinterpret_cast(std::addressof(ts._storage)); +} + +} // namespace Impl + +template +static T* ConstructAt(TypedStorage& ts, Args&&... args) { + return std::construct_at(Impl::GetPointerForConstructAt(ts), std::forward(args)...); +} + +template +static void DestroyAt(TypedStorage& ts) { + return std::destroy_at(GetPointer(ts)); +} + +namespace Impl { + +template +class TypedStorageGuard { + YUZU_NON_COPYABLE(TypedStorageGuard); + +private: + TypedStorage& m_ts; + bool m_active; + +public: + template + TypedStorageGuard(TypedStorage& ts, Args&&... args) : m_ts(ts), m_active(true) { + ConstructAt(m_ts, std::forward(args)...); + } + + ~TypedStorageGuard() { + if (m_active) { + DestroyAt(m_ts); + } + } + + void Cancel() { + m_active = false; + } + + TypedStorageGuard(TypedStorageGuard&& rhs) : m_ts(rhs.m_ts), m_active(rhs.m_active) { + rhs.Cancel(); + } + + TypedStorageGuard& operator=(TypedStorageGuard&& rhs) = delete; +}; + +} // namespace Impl + +template +static Impl::TypedStorageGuard ConstructAtGuarded(TypedStorage& ts, Args&&... args) { + return Impl::TypedStorageGuard(ts, std::forward(args)...); +} + +} // namespace Common