// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include "common/assert.h" namespace Common { namespace detail { template struct TypedStorageImpl { alignas(Align) u8 storage_[Size]; }; } // namespace detail template using TypedStorage = detail::TypedStorageImpl; template static constexpr T* GetPointer(TypedStorage& ts) { return static_cast(static_cast(std::addressof(ts.storage_))); } template static constexpr const T* GetPointer(const TypedStorage& 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; TypedStorage 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 static_cast(static_cast(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 > constexpr 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