703c57a119
Provides the same construct, but makes it obey namespacing.
191 lines
6.8 KiB
C++
191 lines
6.8 KiB
C++
// Copyright 2021 yuzu Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#pragma once
|
|
|
|
#include <type_traits>
|
|
|
|
#include "common/assert.h"
|
|
#include "common/common_types.h"
|
|
|
|
namespace Common {
|
|
namespace detail {
|
|
template <typename T, size_t Size, size_t Align>
|
|
struct TypedStorageImpl {
|
|
std::aligned_storage_t<Size, Align> storage_;
|
|
};
|
|
} // 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 {
|
|
|
|
template <size_t MaxDepth>
|
|
struct OffsetOfUnionHolder {
|
|
template <typename ParentType, typename MemberType, size_t Offset>
|
|
union UnionImpl {
|
|
using PaddingMember = char;
|
|
static constexpr size_t GetOffset() {
|
|
return Offset;
|
|
}
|
|
|
|
#pragma pack(push, 1)
|
|
struct {
|
|
PaddingMember padding[Offset];
|
|
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
|
|
} data;
|
|
#pragma pack(pop)
|
|
UnionImpl<ParentType, MemberType, Offset + 1> next_union;
|
|
};
|
|
|
|
template <typename ParentType, typename MemberType>
|
|
union UnionImpl<ParentType, MemberType, 0> {
|
|
static constexpr size_t GetOffset() {
|
|
return 0;
|
|
}
|
|
|
|
struct {
|
|
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
|
|
} data;
|
|
UnionImpl<ParentType, MemberType, 1> next_union;
|
|
};
|
|
|
|
template <typename ParentType, typename MemberType>
|
|
union UnionImpl<ParentType, MemberType, MaxDepth> {};
|
|
};
|
|
|
|
template <typename ParentType, typename MemberType>
|
|
struct OffsetOfCalculator {
|
|
using UnionHolder =
|
|
typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType,
|
|
0>;
|
|
union Union {
|
|
char c{};
|
|
UnionHolder first_union;
|
|
TypedStorage<ParentType> parent;
|
|
|
|
constexpr Union() : c() {}
|
|
};
|
|
static constexpr Union U = {};
|
|
|
|
static constexpr const MemberType* GetNextAddress(const MemberType* start,
|
|
const MemberType* target) {
|
|
while (start < target) {
|
|
start++;
|
|
}
|
|
return start;
|
|
}
|
|
|
|
static constexpr std::ptrdiff_t GetDifference(const MemberType* start,
|
|
const MemberType* target) {
|
|
return (target - start) * sizeof(MemberType);
|
|
}
|
|
|
|
template <typename CurUnion>
|
|
static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member,
|
|
CurUnion& cur_union) {
|
|
constexpr size_t Offset = CurUnion::GetOffset();
|
|
const auto target = std::addressof(GetPointer(U.parent)->*member);
|
|
const auto start = std::addressof(cur_union.data.members[0]);
|
|
const auto next = GetNextAddress(start, target);
|
|
|
|
if (next != target) {
|
|
if constexpr (Offset < sizeof(MemberType) - 1) {
|
|
return OffsetOfImpl(member, cur_union.next_union);
|
|
} else {
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
return (next - start) * sizeof(MemberType) + Offset;
|
|
}
|
|
|
|
static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
|
|
return OffsetOfImpl(member, U.first_union);
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
struct GetMemberPointerTraits;
|
|
|
|
template <typename P, typename M>
|
|
struct GetMemberPointerTraits<M P::*> {
|
|
using Parent = P;
|
|
using Member = M;
|
|
};
|
|
|
|
template <auto MemberPtr>
|
|
using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
|
|
|
|
template <auto MemberPtr>
|
|
using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
|
|
|
|
template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
|
|
static inline std::ptrdiff_t OffsetOf = [] {
|
|
using DeducedParentType = GetParentType<MemberPtr>;
|
|
using MemberType = GetMemberType<MemberPtr>;
|
|
static_assert(std::is_base_of<DeducedParentType, RealParentType>::value ||
|
|
std::is_same<RealParentType, DeducedParentType>::value);
|
|
|
|
return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
|
|
}();
|
|
|
|
} // namespace impl
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) {
|
|
std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
|
|
return *static_cast<RealParentType*>(
|
|
static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) {
|
|
std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
|
|
return *static_cast<const RealParentType*>(static_cast<const void*>(
|
|
static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) {
|
|
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) {
|
|
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
} // namespace Common
|