在类 C 结构中自动重新排序字段的方法

Posted

技术标签:

【中文标题】在类 C 结构中自动重新排序字段的方法【英文标题】:Approach for automatic fields reordering in C-like structs 【发布时间】:2015-05-21 03:19:36 【问题描述】:

有没有办法在类 C 结构中执行automatic fields reordering?我的意思是使用语言特性,如(C 和 C++ 的预处理器和 C++ 的模板/类型特征/等),这使得执行以下宏(类似 Boost.Fusion 的样式以适应结构)成为可能:

REARRANGE(StructureName,
          (int8_t)(FieldName1),
          (int32_t)(FieldName2),
          (int16_t)(FieldName3),
          (int32_t)(FieldName4));
// is equivalent to (without loss of generality):
struct StructureName


    int32_t FieldName2;
    int32_t FieldName4;
    int16_t FieldName3;
    int8_t FieldName1;

;

当然,方法应考虑字段的alignof 值(连同sizeof),如果可能,还应考虑#pragma pack 当前值。

我知道结果的可移植性不好,但它仅供本地使用。

必须将字段名称与相应的类型一起保存。

目的是减小结构的总大小。

【问题讨论】:

我认为类型的大小在预处理器阶段是未知的。因此名称的位置不能交换。看来,我应该等待内省的语言介绍。 您可以使用可变参数模板和元组来实现这一点,但您只能通过索引/类型访问成员。 @Orient 您可以通过创建一个接收配置文件并输出适当头文件的代码生成器来实现所需的结果。 【参考方案1】:

一些解决方案:

clang-tools-extra 中使用clang-reorder-fields 重新排序字段 让 gcc 使用 -fipa-struct-reorg 自动执行此操作。自 gcc 4.8.x 以来,该选项已被删除,但仍在积极开发中,以便在未来版本中包含回官方 gcc。与此同时,如果你想尝试,你必须使用 gcc SVN 或 github mirror 上的 struct-reorg 分支从源代码构建 gcc

相关:

Automated field re-ordering in C structs to avoid padding Struct Reordering by compiler

【讨论】:

【参考方案2】:

我找到了C++14解决方案:

#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/repetition/enum.hpp>

#include <utility>

#include <cstddef>

namespace details


template< std::size_t /*index*/, typename /*tag*/ >
struct member;

struct pair

    std::size_t k, v; 
    constexpr bool operator < (pair const & r) const  return r.k < k; 
;

constexpr void swap(pair & l, pair & r)  pair m = r; r = l; l = m; 

template< int N >
constexpr
void qsort(pair (&a)[N], int const l, int const r)
 
    int i = l, j = r;
    pair pivot = a[l + (r - l) / 2];
    while (!(j < i))  
        while (a[i] < pivot) ++i;
        while (pivot < a[j]) --j;
        if (!(j < i)) 
            swap(a[i], a[j]);
            ++i;
            --j;
        
    
    if (l < j) qsort(a, l, j);
    if (i < r) qsort(a, i, r);


template< int N >
struct map

    pair a[N];
;

template< int N, std::size_t ...indices >
constexpr
map< N > make_map(pair (&a)[N], std::index_sequence< indices... >)

    return a[indices]...;


template< int N >
constexpr
map< N > qsort(pair (&&a)[N])

    if (1 < N) 
        qsort< N >(a, 0, N - 1);
    
    return make_map< N >(a, std::make_index_sequence< N >);




#define GEN0(z, tag, index, type_name) template<> struct member< index, tag > \
 BOOST_PP_SEQ_HEAD(type_name) BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_TAIL(type_name)); ;

#define GEN1(z, ignored, index, type_name) sizeof(BOOST_PP_SEQ_HEAD(type_name)), index,

#define GEN2(z, index, tags) details::member< BOOST_PP_SEQ_HEAD(tags)::map.a[index].v, BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_TAIL(tags)) >

#define GEN(ns, tag, members) \
namespace ns  struct tag;  \
namespace details  BOOST_PP_SEQ_FOR_EACH_I(GEN0, ns::tag, members)  \
namespace details::tags::ns  struct tag  static constexpr auto map = qsort(BOOST_PP_SEQ_FOR_EACH_I(GEN1, %%, members)); ;  \
namespace ns  struct tag : BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(members), GEN2, (details::tags::ns::tag)(tag)) ; 

struct T  char c[3]; ;

GEN(user::u2, S, ((char)(c))((int)(i))((T)(t)))

#include <cassert>

int main()

    using namespace details;
    void(member< 0, user::u2::S >.c);
    void(member< 1, user::u2::S >.i);
    static_assert(tags::user::u2::S::map.a[0].k == 4);
    static_assert(tags::user::u2::S::map.a[1].k == 3);
    static_assert(tags::user::u2::S::map.a[2].k == 1);
    user::u2::S s4, 'a', 'b', 'c', 'd';
    assert((void *)&s.i == (void *)&s);
    assert((void *)&s.t < (void *)&s.c);
    static_assert(sizeof(s) == 8);

【讨论】:

【参考方案3】: 对于 MS 编译器,使用 #pragma pack,(#pragma pack(1) 消除了所有空白)。并检查链接以获取详细信息,因为不能保证此指令始终有效。 对于 GCC,有 __attribute__ ((packed))

由于您的唯一目标是尽可能减少内存中数据的大小,因此这正是您所需要的。

【讨论】:

不是唯一的目标是总大小,而是所有数据成员的正确对齐。

以上是关于在类 C 结构中自动重新排序字段的方法的主要内容,如果未能解决你的问题,请参考以下文章

C中的结构内存布局

php Google地址自动填充字段重新排序#plugin #checkout

是否有允许结构重新排序的 GCC 关键字?

根据条件c ++重新排序priority_queue

msSQL 根据2个条件进行重新排序并增加一个排序字段

在列表中重新排序列表值的正确方法是啥?