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