From fb43b8efd22eaf0eaccf0c9ddc70cf2e06deafeb Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 27 Dec 2020 01:58:16 -0800 Subject: common: Introduce useful tree structures. --- src/common/parent_of_member.h | 189 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 src/common/parent_of_member.h (limited to 'src/common/parent_of_member.h') diff --git a/src/common/parent_of_member.h b/src/common/parent_of_member.h new file mode 100644 index 000000000..1af31ee44 --- /dev/null +++ b/src/common/parent_of_member.h @@ -0,0 +1,189 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include "common/assert.h" +#include "common/common_types.h" + +namespace Common { + +template +struct TypedStorage { + std::aligned_storage_t storage_; +}; + +#define TYPED_STORAGE(...) TypedStorage<__VA_ARGS__, sizeof(__VA_ARGS__), alignof(__VA_ARGS__)> + +template +static constexpr T* GetPointer(TYPED_STORAGE(T) & ts) { + return static_cast(static_cast(std::addressof(ts.storage_))); +} + +template +static constexpr const T* GetPointer(const TYPED_STORAGE(T) & ts) { + return static_cast(static_cast(std::addressof(ts.storage_))); +} + +namespace impl { + +template +struct OffsetOfUnionHolder { + template + 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 next_union; + }; + + template + union UnionImpl { + static constexpr size_t GetOffset() { + return 0; + } + + struct { + MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1]; + } data; + UnionImpl next_union; + }; + + template + union UnionImpl {}; +}; + +template +struct OffsetOfCalculator { + using UnionHolder = + typename OffsetOfUnionHolder::template UnionImpl; + union Union { + char c{}; + UnionHolder first_union; + TYPED_STORAGE(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 + 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 +struct GetMemberPointerTraits; + +template +struct GetMemberPointerTraits { + using Parent = P; + using Member = M; +}; + +template +using GetParentType = typename GetMemberPointerTraits::Parent; + +template +using GetMemberType = typename GetMemberPointerTraits::Member; + +template > +static inline std::ptrdiff_t OffsetOf = [] { + using DeducedParentType = GetParentType; + using MemberType = GetMemberType; + static_assert(std::is_base_of::value || + std::is_same::value); + + return OffsetOfCalculator::OffsetOf(MemberPtr); +}(); + +} // namespace impl + +template > +constexpr RealParentType& GetParentReference(impl::GetMemberType* member) { + std::ptrdiff_t Offset = impl::OffsetOf; + return *static_cast( + static_cast(static_cast(static_cast(member)) - Offset)); +} + +template > +constexpr RealParentType const& GetParentReference(impl::GetMemberType const* member) { + std::ptrdiff_t Offset = impl::OffsetOf; + return *static_cast(static_cast( + static_cast(static_cast(member)) - Offset)); +} + +template > +constexpr RealParentType* GetParentPointer(impl::GetMemberType* member) { + return std::addressof(GetParentReference(member)); +} + +template > +constexpr RealParentType const* GetParentPointer(impl::GetMemberType const* member) { + return std::addressof(GetParentReference(member)); +} + +template > +constexpr RealParentType& GetParentReference(impl::GetMemberType& member) { + return GetParentReference(std::addressof(member)); +} + +template > +constexpr RealParentType const& GetParentReference(impl::GetMemberType const& member) { + return GetParentReference(std::addressof(member)); +} + +template > +constexpr RealParentType* GetParentPointer(impl::GetMemberType& member) { + return std::addressof(GetParentReference(member)); +} + +template > +constexpr RealParentType const* GetParentPointer(impl::GetMemberType const& member) { + return std::addressof(GetParentReference(member)); +} + +} // namespace Common -- cgit v1.2.3