/* * Copyright (c) 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 #include namespace ams::util { namespace impl { #define AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT 1 #if AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT template consteval std::strong_ordering TestOffsetForOffsetOfImpl() { #pragma pack(push, 1) const union Union { char c; struct { char padding[Offset]; M members[1 + (sizeof(P) / std::max(sizeof(M), 1))]; }; P p; constexpr Union() : c() { /* ... */ } constexpr ~Union() { /* ... */ } } U; #pragma pack(pop) const M *target = std::addressof(U.p.*Ptr); const M *guess = std::addressof(U.members[0]); /* NOTE: target == guess is definitely legal, target < guess is probably legal, definitely legal if Offset <= true offsetof. */ /* <=> may or may not be legal, but it definitely seems to work. Evaluate again, if it breaks. */ return guess <=> target; //if (guess == target) { // return std::strong_ordering::equal; //} else if (guess < target) { // return std::strong_ordering::less; //} else { // return std::strong_ordering::greater; //} } template consteval std::ptrdiff_t OffsetOfImpl() { static_assert(Low <= High); constexpr std::ptrdiff_t Guess = (Low + High) / 2; constexpr auto Order = TestOffsetForOffsetOfImpl(); if constexpr (Order == std::strong_ordering::equal) { return Guess; } else if constexpr (Order == std::strong_ordering::less) { return OffsetOfImpl(); } else { static_assert(Order == std::strong_ordering::greater); return OffsetOfImpl(); } } template struct OffsetOfCalculator { static constexpr const std::ptrdiff_t Value = OffsetOfImpl<0, sizeof(P), P, M, Ptr>(); }; #else template struct OffsetOfCalculator { private: static consteval std::ptrdiff_t Calculate() { const union Union { ParentType p; char c; constexpr Union() : c() { /* ... */ } constexpr ~Union() { /* ... */ } } U; const auto *parent = std::addressof(U.p); const auto *target = std::addressof(parent->*Ptr); return static_cast(static_cast(target)) - static_cast(static_cast(parent)); } public: static constexpr const std::ptrdiff_t Value = Calculate(); }; #endif 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> requires (std::derived_from> || std::same_as>) struct OffsetOf : public std::integral_constant, MemberPtr>::Value> {}; } template> ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType *member) { constexpr std::ptrdiff_t Offset = impl::OffsetOf::value; return *static_cast(static_cast(static_cast(static_cast(member)) - Offset)); } template> ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType const *member) { constexpr std::ptrdiff_t Offset = impl::OffsetOf::value; return *static_cast(static_cast(static_cast(static_cast(member)) - Offset)); } template> ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType *member) { return std::addressof(GetParentReference(member)); } template> ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType const *member) { return std::addressof(GetParentReference(member)); } template> ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType &member) { return GetParentReference(std::addressof(member)); } template> ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType const &member) { return GetParentReference(std::addressof(member)); } template> ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType &member) { return std::addressof(GetParentReference(member)); } template> ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType const &member) { return std::addressof(GetParentReference(member)); } /* Defines, for use by other code. */ #define AMS_OFFSETOF(parent, member) (__builtin_offsetof(parent, member)) #define AMS_GET_PARENT_PTR(parent, member, _arg) (::ams::util::GetParentPointer<&parent::member, parent>(_arg)) #define AMS_GET_PARENT_REF(parent, member, _arg) (::ams::util::GetParentReference<&parent::member, parent>(_arg)) }