summaryrefslogtreecommitdiffstats
path: root/src/common/parent_of_member.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/parent_of_member.h')
-rw-r--r--src/common/parent_of_member.h191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h
new file mode 100644
index 000000000..d9a14529d
--- /dev/null
+++ b/src/common/parent_of_member.h
@@ -0,0 +1,191 @@
+// 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