/*
* 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))
}