mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 21:01:13 +00:00
207 lines
9.4 KiB
C++
207 lines
9.4 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
#include <vapours/common.hpp>
|
|
#include <vapours/assert.hpp>
|
|
#include <vapours/util/util_typed_storage.hpp>
|
|
|
|
namespace ams::util {
|
|
|
|
namespace impl {
|
|
|
|
#define AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT 1
|
|
|
|
#if AMS_UTIL_OFFSET_OF_STANDARD_COMPLIANT
|
|
|
|
template<std::ptrdiff_t Offset, typename P, typename M, auto Ptr>
|
|
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<size_t>(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<std::ptrdiff_t Low, std::ptrdiff_t High, typename P, typename M, auto Ptr>
|
|
consteval std::ptrdiff_t OffsetOfImpl() {
|
|
static_assert(Low <= High);
|
|
|
|
constexpr std::ptrdiff_t Guess = (Low + High) / 2;
|
|
constexpr auto Order = TestOffsetForOffsetOfImpl<Guess, P, M, Ptr>();
|
|
|
|
if constexpr (Order == std::strong_ordering::equal) {
|
|
return Guess;
|
|
} else if constexpr (Order == std::strong_ordering::less) {
|
|
return OffsetOfImpl<Guess + 1, High, P, M, Ptr>();
|
|
} else {
|
|
static_assert(Order == std::strong_ordering::greater);
|
|
return OffsetOfImpl<Low, Guess - 1, P, M, Ptr>();
|
|
}
|
|
}
|
|
|
|
template<typename P, typename M, auto Ptr>
|
|
struct OffsetOfCalculator {
|
|
static constexpr const std::ptrdiff_t Value = OffsetOfImpl<0, sizeof(P), P, M, Ptr>();
|
|
};
|
|
|
|
#else
|
|
|
|
template<typename ParentType, typename MemberType, auto Ptr>
|
|
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<const uint8_t *>(static_cast<const void *>(target)) - static_cast<const uint8_t *>(static_cast<const void *>(parent));
|
|
}
|
|
public:
|
|
static constexpr const std::ptrdiff_t Value = Calculate();
|
|
};
|
|
|
|
#endif
|
|
|
|
template<typename T>
|
|
struct GetMemberPointerTraits;
|
|
|
|
template<typename P, typename M>
|
|
struct GetMemberPointerTraits<M P::*> {
|
|
using Parent = P;
|
|
using Member = M;
|
|
};
|
|
|
|
template<auto MemberPtr>
|
|
using GetParentType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Parent;
|
|
|
|
template<auto MemberPtr>
|
|
using GetMemberType = typename GetMemberPointerTraits<decltype(MemberPtr)>::Member;
|
|
|
|
template<auto MemberPtr, typename RealParentType = GetParentType<MemberPtr>> requires (std::derived_from<RealParentType, GetParentType<MemberPtr>> || std::same_as<RealParentType, GetParentType<MemberPtr>>)
|
|
struct OffsetOf : public std::integral_constant<std::ptrdiff_t, OffsetOfCalculator<RealParentType, GetMemberType<MemberPtr>, MemberPtr>::Value> {};
|
|
|
|
#if defined(ATMOSPHERE_COMPILER_CLANG)
|
|
template<typename ParentType, typename MemberType, auto Ptr>
|
|
struct OffsetOfCalculatorTheSadWayForClangSupport {
|
|
static ALWAYS_INLINE std::ptrdiff_t Calculate() {
|
|
const union Union {
|
|
ParentType p;
|
|
char c;
|
|
|
|
Union() : c() { /* ... */ }
|
|
~Union() { /* ... */ }
|
|
} U;
|
|
|
|
const auto *parent = std::addressof(U.p);
|
|
const auto *target = std::addressof(parent->*Ptr);
|
|
|
|
return static_cast<const uint8_t *>(static_cast<const void *>(target)) - static_cast<const uint8_t *>(static_cast<const void *>(parent));
|
|
}
|
|
};
|
|
#endif
|
|
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType<MemberPtr> *member) {
|
|
/* TODO: If clang resolves compiler-bugs in consteval (or if my std proposal makes it into a future C++ standard), we should go back to constexpr. */
|
|
#if defined(ATMOSPHERE_COMPILER_CLANG)
|
|
const std::ptrdiff_t Offset = impl::OffsetOfCalculatorTheSadWayForClangSupport<RealParentType, impl::GetMemberType<MemberPtr>, MemberPtr>::Calculate();
|
|
#else
|
|
constexpr std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>::value;
|
|
#endif
|
|
return *static_cast<RealParentType *>(static_cast<void *>(static_cast<uint8_t *>(static_cast<void *>(member)) - Offset));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType<MemberPtr> const *member) {
|
|
/* TODO: If clang resolves compiler-bugs in consteval (or if my std proposal makes it into a future C++ standard), we should go back to constexpr. */
|
|
#if defined(ATMOSPHERE_COMPILER_CLANG)
|
|
const std::ptrdiff_t Offset = impl::OffsetOfCalculatorTheSadWayForClangSupport<RealParentType, impl::GetMemberType<MemberPtr>, MemberPtr>::Calculate();
|
|
#else
|
|
constexpr std::ptrdiff_t Offset = impl::OffsetOf<MemberPtr, RealParentType>::value;
|
|
#endif
|
|
return *static_cast<const RealParentType *>(static_cast<const void *>(static_cast<const uint8_t *>(static_cast<const void *>(member)) - Offset));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType<MemberPtr> *member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType<MemberPtr> const *member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType &GetParentReference(impl::GetMemberType<MemberPtr> &member) {
|
|
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType const &GetParentReference(impl::GetMemberType<MemberPtr> const &member) {
|
|
return GetParentReference<MemberPtr, RealParentType>(std::addressof(member));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType *GetParentPointer(impl::GetMemberType<MemberPtr> &member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(member));
|
|
}
|
|
|
|
template<auto MemberPtr, typename RealParentType = impl::GetParentType<MemberPtr>>
|
|
ALWAYS_INLINE RealParentType const *GetParentPointer(impl::GetMemberType<MemberPtr> const &member) {
|
|
return std::addressof(GetParentReference<MemberPtr, RealParentType>(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))
|
|
|
|
}
|