如何计算带#pragma pack的POD结构体成员偏移(基于类型列表)
计算打包POD结构体成员偏移量的问题
有方法可以在考虑内存对齐的前提下,计算std::tuple类型列表中某类型的偏移量,但我想知道如何让这个逻辑同时支持被#pragma pack打包的POD结构体——这类结构体没有使用标准对齐规则。
以下代码能正确计算非打包结构体的成员偏移,但对打包结构体失效:
#include <cstddef> #include <cstdint> #include <tuple> template <typename /*CRTP*/> struct calc_offset; template <template <size_t> class ElemTraits> struct calc_offset<ElemTraits<0>> { constexpr static size_t value = 0; }; template <template <size_t> class ElemTraits, size_t Index> struct calc_offset<ElemTraits<Index>> { constexpr static size_t _get_value() noexcept { constexpr auto roundUp = [](size_t num, size_t multiple) -> size_t { const size_t mod = num % multiple; return mod == 0 ? num : num + multiple - mod; }; using prev_traits = ElemTraits<Index - 1>; const size_t type_alignment = std::alignment_of<typename ElemTraits<Index>::value_type>::value, prev_type_size = sizeof(typename prev_traits::value_type), prev_offset = prev_traits::offset; return roundUp(prev_offset + prev_type_size, type_alignment); } constexpr static size_t value = _get_value(); }; // not a real structure template <typename POD, typename Fields> struct pod_traits { template <size_t I> struct element { using value_type = std::tuple_element_t<I, Fields>; using pod_type = POD; constexpr static size_t offset = calc_offset<element<I>>::value; }; }; struct normal { uint8_t _0; uint64_t _1; uint8_t _2; uint16_t _3; }; using normal_types = std::tuple<decltype(normal{}._0), decltype(normal{}._1), decltype(normal{}._2), decltype(normal{}._3)>; using normal_traits = pod_traits<normal, normal_types>; static_assert(offsetof(normal, _0) == normal_traits::element<0>::offset); static_assert(offsetof(normal, _1) == normal_traits::element<1>::offset); static_assert(offsetof(normal, _2) == normal_traits::element<2>::offset); static_assert(offsetof(normal, _3) == normal_traits::element<3>::offset); #pragma pack(push, 1) struct packed_1 { uint8_t _0; uint64_t _1; uint8_t _2; uint16_t _3; }; #pragma pack(pop) using packed_1_types = std::tuple<decltype(packed_1{}._0), decltype(packed_1{}._1), decltype(packed_1{}._2), decltype(packed_1{}._3)>; using packed_1_traits = pod_traits<packed_1, packed_1_types>; static_assert(offsetof(packed_1, _0) == packed_1_traits::element<0>::offset); // these fail // static_assert(offsetof(packed_1, _1) == packed_1_traits::element<1>::offset); // static_assert(offsetof(packed_1, _2) == packed_1_traits::element<2>::offset); // static_assert(offsetof(packed_1, _3) == packed_1_traits::element<3>::offset);
我尝试对代码做了如下修改,将对齐边界改为类型自身对齐和结构体对齐的较小值,所有断言都通过了:
const size_t type_alignment = std::alignment_of<typename ElemTraits<Index>::value_type>::value, prev_type_size = sizeof(typename prev_traits::value_type), prev_offset = prev_traits::offset, pod_alignment = std::alignment_of<typename ElemTraits<Index>::pod_type>::value; return roundUp(prev_offset + prev_type_size, std::min(type_alignment, pod_alignment));
内容的提问来源于stack exchange,提问作者Sergey Kolesnik
相关产品推荐
相关产品推荐

