diff options
Diffstat (limited to 'src/common/parent_of_member.h')
| -rw-r--r-- | src/common/parent_of_member.h | 191 |
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 | |||
| 12 | namespace Common { | ||
| 13 | namespace detail { | ||
| 14 | template <typename T, size_t Size, size_t Align> | ||
| 15 | struct TypedStorageImpl { | ||
| 16 | std::aligned_storage_t<Size, Align> storage_; | ||
| 17 | }; | ||
| 18 | } // namespace detail | ||
| 19 | |||
| 20 | template <typename T> | ||
| 21 | using TypedStorage = detail::TypedStorageImpl<T, sizeof(T), alignof(T)>; | ||
| 22 | |||
| 23 | template <typename T> | ||
| 24 | static constexpr T* GetPointer(TypedStorage<T>& ts) { | ||
| 25 | return static_cast<T*>(static_cast<void*>(std::addressof(ts.storage_))); | ||
| 26 | } | ||
| 27 | |||
| 28 | template <typename T> | ||
| 29 | static constexpr const T* GetPointer(const TypedStorage<T>& ts) { | ||
| 30 | return static_cast<const T*>(static_cast<const void*>(std::addressof(ts.storage_))); | ||
| 31 | } | ||
| 32 | |||
| 33 | namespace impl { | ||
| 34 | |||
| 35 | template <size_t MaxDepth> | ||
| 36 | struct 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 | |||
| 69 | template <typename ParentType, typename MemberType> | ||
| 70 | struct 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 | |||
| 120 | template <typename T> | ||
| 121 | struct GetMemberPointerTraits; | ||
| 122 | |||
| 123 | template <typename P, typename M> | ||
| 124 | struct GetMemberPointerTraits<M P::*> { | ||
| 125 | using Parent = P; | ||
| 126 | using Member = M; | ||
| 127 | }; | ||
| 128 | |||
| 129 | template <auto MemberPtr> | ||
| 130 | using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent; | ||
| 131 | |||
| 132 | template <auto MemberPtr> | ||
| 133 | using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member; | ||
| 134 | |||
| 135 | template <auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>> | ||
| 136 | static 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 | |||
| 147 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 148 | constexpr 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 | |||
| 154 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 155 | constexpr 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 | |||
| 161 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 162 | constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>* member) { | ||
| 163 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 164 | } | ||
| 165 | |||
| 166 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 167 | constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const* member) { | ||
| 168 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 169 | } | ||
| 170 | |||
| 171 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 172 | constexpr RealParentType& GetParentReference(impl::GetMemberType<MemberPtr>& member) { | ||
| 173 | return GetParentReference<MemberPtr, RealParentType>(std::addressof(member)); | ||
| 174 | } | ||
| 175 | |||
| 176 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 177 | constexpr RealParentType const& GetParentReference(impl::GetMemberType<MemberPtr> const& member) { | ||
| 178 | return GetParentReference<MemberPtr, RealParentType>(std::addressof(member)); | ||
| 179 | } | ||
| 180 | |||
| 181 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 182 | constexpr RealParentType* GetParentPointer(impl::GetMemberType<MemberPtr>& member) { | ||
| 183 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 184 | } | ||
| 185 | |||
| 186 | template <auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>> | ||
| 187 | constexpr RealParentType const* GetParentPointer(impl::GetMemberType<MemberPtr> const& member) { | ||
| 188 | return std::addressof(GetParentReference<MemberPtr, RealParentType>(member)); | ||
| 189 | } | ||
| 190 | |||
| 191 | } // namespace Common | ||