/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include "common/assert.h" #include "common/common_types.h" namespace Common { template struct TypedStorage { typename std::aligned_storage::type _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 { /* Empty ... */ }; }; template struct OffsetOfCalculator { using UnionHolder = typename OffsetOfUnionHolder::template UnionImpl; union Union { char c; UnionHolder first_union; TYPED_STORAGE(ParentType) parent; /* This coerces the active member to be c. */ 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