You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算带#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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 02:02:24