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