common: Add various utility classes

This commit is contained in:
FearlessTobi 2024-02-22 16:26:54 +01:00
parent a93d249ac1
commit 12e55ae31b
5 changed files with 161 additions and 19 deletions

View File

@ -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

20
src/common/enum_util.h Normal file
View File

@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
namespace Common {
template <typename Enum>
requires std::is_enum<Enum>::value
constexpr typename std::underlying_type<Enum>::type ToUnderlying(Enum e) {
return static_cast<typename std::underlying_type<Enum>::type>(e);
}
template <typename Enum>
requires std::is_enum<Enum>::value
constexpr Enum FromUnderlying(typename std::underlying_type<Enum>::type v) {
return static_cast<Enum>(v);
}
} // namespace Common

View File

@ -6,27 +6,9 @@
#include <type_traits>
#include "common/assert.h"
#include "common/typed_storage.h"
namespace Common {
namespace detail {
template <typename T, size_t Size, size_t Align>
struct TypedStorageImpl {
alignas(Align) u8 storage_[Size];
};
} // namespace detail
template <typename T>
using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>;
template <typename T>
static constexpr T* GetPointer(TypedStorage<T>& ts) {
return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_)));
}
template <typename T>
static constexpr const T* GetPointer(const TypedStorage<T>& ts) {
return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_)));
}
namespace impl {

View File

@ -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 <std::unsigned_integral U>
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<U, U>::value);
}
}
template <std::integral T>
constexpr T ConvertToLittleEndian(const T val) {
using U = typename std::make_unsigned<T>::type;
if constexpr (IsBigEndian()) {
return static_cast<T>(SwapEndian<U>(static_cast<U>(val)));
} else {
static_assert(IsLittleEndian());
return static_cast<T>(static_cast<U>(val));
}
}
template <std::integral T>
constexpr T LoadLittleEndian(const T* ptr) {
return ConvertToLittleEndian<T>(*ptr);
}
} // Namespace Common
template <typename T, typename F>

View File

@ -0,0 +1,96 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <memory>
#include <utility>
#include "common/common_funcs.h"
namespace Common {
template <typename T, size_t Size = sizeof(T), size_t Align = alignof(T)>
struct TypedStorage {
typename std::aligned_storage<Size, Align>::type _storage;
};
template <typename T>
static T* GetPointer(TypedStorage<T>& ts) {
return std::launder(reinterpret_cast<T*>(std::addressof(ts._storage)));
}
template <typename T>
static const T* GetPointer(const TypedStorage<T>& ts) {
return std::launder(reinterpret_cast<const T*>(std::addressof(ts._storage)));
}
template <typename T>
static T& GetReference(TypedStorage<T>& ts) {
return *GetPointer(ts);
}
template <typename T>
static const T& GetReference(const TypedStorage<T>& ts) {
return *GetPointer(ts);
}
namespace Impl {
template <typename T>
static T* GetPointerForConstructAt(TypedStorage<T>& ts) {
return reinterpret_cast<T*>(std::addressof(ts._storage));
}
} // namespace Impl
template <typename T, typename... Args>
static T* ConstructAt(TypedStorage<T>& ts, Args&&... args) {
return std::construct_at(Impl::GetPointerForConstructAt(ts), std::forward<Args>(args)...);
}
template <typename T>
static void DestroyAt(TypedStorage<T>& ts) {
return std::destroy_at(GetPointer(ts));
}
namespace Impl {
template <typename T>
class TypedStorageGuard {
YUZU_NON_COPYABLE(TypedStorageGuard);
private:
TypedStorage<T>& m_ts;
bool m_active;
public:
template <typename... Args>
TypedStorageGuard(TypedStorage<T>& ts, Args&&... args) : m_ts(ts), m_active(true) {
ConstructAt(m_ts, std::forward<Args>(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 <typename T, typename... Args>
static Impl::TypedStorageGuard<T> ConstructAtGuarded(TypedStorage<T>& ts, Args&&... args) {
return Impl::TypedStorageGuard<T>(ts, std::forward<Args>(args)...);
}
} // namespace Common