offsetof: proper detection when sizeof() != alignof

This commit is contained in:
Michael Scire 2020-01-16 04:09:11 -08:00
parent 769a57526c
commit 9068e2071c

View file

@ -22,14 +22,15 @@ namespace ams::util {
namespace impl {
template<size_t Alignment>
template<size_t MaxDepth>
struct OffsetOfUnionHolder {
template<typename ParentType, typename MemberType, size_t Offset>
union UnionImpl {
using PaddingMember = std::array<char, alignof(ParentType)>;
static constexpr size_t GetOffset() { return Offset; }
struct {
char padding[Offset];
PaddingMember padding[Offset];
MemberType members[(sizeof(ParentType) / sizeof(MemberType)) + 1];
} data;
UnionImpl<ParentType, MemberType, Offset + 1> next_union;
@ -46,12 +47,12 @@ namespace ams::util {
};
template<typename ParentType, typename MemberType>
union UnionImpl<ParentType, MemberType, Alignment> { /* Empty */ };
union UnionImpl<ParentType, MemberType, MaxDepth> { /* Empty */ };
};
template<typename ParentType, typename MemberType>
struct OffsetOfCalculator {
using UnionHolder = typename OffsetOfUnionHolder<alignof(MemberType)>::template UnionImpl<ParentType, MemberType, 0>;
using UnionHolder = typename OffsetOfUnionHolder<sizeof(MemberType) / alignof(MemberType) + 1>::template UnionImpl<ParentType, MemberType, 0>;
union Union {
char c;
UnionHolder first_union;
@ -80,15 +81,15 @@ namespace ams::util {
const auto start = std::addressof(cur_union.data.members[0]);
const auto next = GetNextAddress(start, target);
if (next < target) {
if constexpr (Offset + 1 < alignof(MemberType)) {
if (next != target) {
if constexpr (Offset < sizeof(MemberType) / alignof(MemberType)) {
return OffsetOfImpl(member, cur_union.next_union);
} else {
static_assert(Offset + 1 <= alignof(MemberType));
std::abort();
}
}
return (next - start) * sizeof(MemberType) + Offset;
return (next - start) * sizeof(MemberType) + Offset * alignof(MemberType);
}