/* * 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 "../defines.hpp" #include "util_typed_storage.hpp" namespace ams::util { 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 { __builtin_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> constexpr inline std::ptrdiff_t OffsetOf = [] { using DeducedParentType = GetParentType; using MemberType = GetMemberType; static_assert(std::is_base_of::value || std::is_same::value); static_assert(std::is_literal_type::value); return OffsetOfCalculator::OffsetOf(MemberPtr); }(); } template> constexpr ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType *member) { constexpr std::ptrdiff_t Offset = impl::OffsetOf; return *static_cast(static_cast(static_cast(static_cast(member)) - Offset)); } template> constexpr ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType const *member) { constexpr std::ptrdiff_t Offset = impl::OffsetOf; return *static_cast(static_cast(static_cast(static_cast(member)) - Offset)); } template> constexpr ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType *member) { return std::addressof(GetParentReference(member)); } template> constexpr ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType const *member) { return std::addressof(GetParentReference(member)); } template> constexpr ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType &member) { return GetParentReference(std::addressof(member)); } template> constexpr ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType const &member) { return GetParentReference(std::addressof(member)); } template> constexpr ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType &member) { return std::addressof(GetParentReference(member)); } template> constexpr ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType const &member) { return std::addressof(GetParentReference(member)); } /* Defines, for use by other code. */ #define OFFSETOF(parent, member) (::ams::util::impl::OffsetOf<&parent::member, parent>) #define GET_PARENT_PTR(parent, member, _arg) (::ams::util::GetParentPointer<&parent::member, parent>(_arg)) #define GET_PARENT_REF(parent, member, _arg) (::ams::util::GetParentReference<&parent::member, parent>(_arg)) namespace test { struct Struct1 { uint32_t a; }; struct Struct2 { uint32_t b; }; struct Struct3 : public Struct1, Struct2 { uint32_t c; }; static_assert(impl::OffsetOf<&Struct1::a> == 0); static_assert(impl::OffsetOf<&Struct2::b> == 0); static_assert(impl::OffsetOf<&Struct3::a> == 0); static_assert(impl::OffsetOf<&Struct3::b> == 0); static_assert(impl::OffsetOf<&Struct3::a, Struct3> == 0 || impl::OffsetOf<&Struct3::b, Struct3> == 0); static_assert(impl::OffsetOf<&Struct3::a, Struct3> == sizeof(Struct2) || impl::OffsetOf<&Struct3::b, Struct3> == sizeof(Struct1)); static_assert(impl::OffsetOf<&Struct3::c> == sizeof(Struct1) + sizeof(Struct2)); constexpr Struct3 TestStruct3 = {}; static_assert(std::addressof(TestStruct3) == GET_PARENT_PTR(Struct3, a, TestStruct3.a)); static_assert(std::addressof(TestStruct3) == GET_PARENT_PTR(Struct3, a, std::addressof(TestStruct3.a))); static_assert(std::addressof(TestStruct3) == GET_PARENT_PTR(Struct3, b, TestStruct3.b)); static_assert(std::addressof(TestStruct3) == GET_PARENT_PTR(Struct3, b, std::addressof(TestStruct3.b))); static_assert(std::addressof(TestStruct3) == GET_PARENT_PTR(Struct3, c, TestStruct3.c)); static_assert(std::addressof(TestStruct3) == GET_PARENT_PTR(Struct3, c, std::addressof(TestStruct3.c))); struct CharArray { char c0; char c1; char c2; char c3; char c4; char c5; char c6; char c7; }; static_assert(impl::OffsetOf<&CharArray::c0> == 0); static_assert(impl::OffsetOf<&CharArray::c1> == 1); static_assert(impl::OffsetOf<&CharArray::c2> == 2); static_assert(impl::OffsetOf<&CharArray::c3> == 3); static_assert(impl::OffsetOf<&CharArray::c4> == 4); static_assert(impl::OffsetOf<&CharArray::c5> == 5); static_assert(impl::OffsetOf<&CharArray::c6> == 6); static_assert(impl::OffsetOf<&CharArray::c7> == 7); constexpr CharArray TestCharArray = {}; static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c0, TestCharArray.c0)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c0, std::addressof(TestCharArray.c0))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c1, TestCharArray.c1)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c1, std::addressof(TestCharArray.c1))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c2, TestCharArray.c2)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c2, std::addressof(TestCharArray.c2))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c3, TestCharArray.c3)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c3, std::addressof(TestCharArray.c3))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c4, TestCharArray.c4)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c4, std::addressof(TestCharArray.c4))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c5, TestCharArray.c5)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c5, std::addressof(TestCharArray.c5))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c6, TestCharArray.c6)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c6, std::addressof(TestCharArray.c6))); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c7, TestCharArray.c7)); static_assert(std::addressof(TestCharArray) == GET_PARENT_PTR(CharArray, c7, std::addressof(TestCharArray.c7))); } }