summaryrefslogtreecommitdiff
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 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <type_traits>
8
9#include "common/assert.h"
10#include "common/common_types.h"
11
12namespace Common {
13namespace detail {
14template <typename T, size_t Size, size_t Align>
15struct TypedStorageImpl {
16 std::aligned_storage_t<Size, Align> storage_;
17};
18} // namespace detail
19
20template <typename T>
21using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>;
22
23template <typename T>
24static constexpr T* GetPointer(TypedStorage<T>& ts) {
25 return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_)));
26}
27
28template <typename T>
29static constexpr const T* GetPointer(const TypedStorage<T>& ts) {
30 return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_)));
31}
32
33namespace impl {
34
35template <size_t MaxDepth>
36struct OffsetOfUnionHolder {
37 template <typename ParentType, typename MemberType, size_t Offset>
38 union UnionImpl {
39 using PaddingMember = char;
40 static constexpr size_t GetOffset() {
41 return Offset;
42 }
43
44#pragma pack(push, 1)
45 struct {
46 PaddingMember padding[Offset];
47 MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
48 } data;
49#pragma pack(pop)
50 UnionImpl<ParentType, MemberType, Offset + 1> next_union;
51 };
52
53 template <typename ParentType, typename MemberType>
54 union UnionImpl<ParentType, MemberType, 0> {
55 static constexpr size_t GetOffset() {
56 return 0;
57 }
58
59 struct {
60 MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
61 } data;
62 UnionImpl<ParentType, MemberType, 1> next_union;
63 };
64
65 template <typename ParentType, typename MemberType>
66 union UnionImpl<ParentType, MemberType, MaxDepth> {};
67};
68
69template <typename ParentType, typename MemberType>
70struct OffsetOfCalculator {
71 using UnionHolder =
72 typename OffsetOfUnionHolder<sizeof(MemberType)>::template UnionImpl<ParentType, MemberType,
73 0>;
74 union Union {
75 char c{};
76 UnionHolder first_union;
77 TypedStorage<ParentType> parent;
78
79 constexpr Union() : c() {}
80 };
81 static constexpr Union U = {};
82
83 static constexpr const MemberType* GetNextAddress(const MemberType* start,
84 const MemberType* target) {
85 while (start < target) {
86 start++;
87 }
88 return start;
89 }
90
91 static constexpr std::ptrdiff_t GetDifference(const MemberType* start,
92 const MemberType* target) {
93 return (target - start) * sizeof(MemberType);
94 }
95
96 template <typename CurUnion>
97 static constexpr std::ptrdiff_t OffsetOfImpl(MemberType ParentType::*member,
98 CurUnion& cur_union) {
99 constexpr size_t Offset = CurUnion::GetOffset();
100 const auto target = std::addressof(GetPointer(U.parent)->*member);
101 const auto start = std::addressof(cur_union.data.members[0]);
102 const auto next = GetNextAddress(start, target);
103
104 if (next != target) {
105 if constexpr (Offset < sizeof(MemberType) - 1) {
106 return OffsetOfImpl(member, cur_union.next_union);
107 } else {
108 UNREACHABLE();
109 }
110 }
111
112 return (next - start) * sizeof(MemberType) + Offset;
113 }
114
115 static constexpr std::ptrdiff_t OffsetOf(MemberType ParentType::*member) {
116 return OffsetOfImpl(member, U.first_union);
117 }
118};
119
120template <typename T>
121struct GetMemberPointerTraits;
122
123template <typename P, typename M>
124struct GetMemberPointerTraits<M P::*> {
125 using Parent = P;
126 using Member = M;
127};
128
129template <auto MemberPtr>
130using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
131
132template <auto MemberPtr>
133using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
134
135template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>>
136static inline std::ptrdiff_t OffsetOf = [] {
137 using DeducedParentType = GetParentType<MemberPtr>;
138 using MemberType = GetMemberType<MemberPtr>;
139 static_assert(std::is_base_of<DeducedParentType, RealParentType>::value ||
140 std::is_same<RealParentType, DeducedParentType>::value);
141
142 return OffsetOfCalculator<RealParentType, MemberType>::OffsetOf(MemberPtr);
143}();
144
145} // namespace impl
146
147template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
148constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>* member) {
149 std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
150 return *static_cast<RealParentType*>(
151 static_cast<void*>(static_cast<uint8_t*>(static_cast<void*>(member)) - Offset));
152}
153
154template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
155constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const* member) {
156 std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>;
157 return *static_cast<const RealParentType*>(static_cast<const void*>(
158 static_cast<const uint8_t*>(static_cast<const void*>(member)) - Offset));
159}
160
161template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
162constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) {
163 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
164}
165
166template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
167constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) {
168 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
169}
170
171template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
172constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) {
173 return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
174}
175
176template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
177constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) {
178 return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
179}
180
181template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
182constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) {
183 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
184}
185
186template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
187constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) {
188 return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
189}
190
191} // namespace Common