Approach for automatic fields reordering in C-like structs

天涯浪子 提交于 2019-12-05 00:21:20


Is there a way to perform automatic fields reordering in C-like structs? I mean the using of the language features like( preprocessor for C and C++ and templates/type traits/etc for C++), which make it possible to do the following macro (Boost.Fusion-like style to adapt structures):

// is equivalent to (without loss of generality):
struct StructureName

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


Of course, approach should take into account alignof values (together with sizeof) for fields and, if it possible, #pragma pack current value.

I am aware of bad portability of the result, but it for local use only.

It is mandatory thing to save the field names together with respective types.

The aim is to reduce total structure size.


I found the C++14 solution:

#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 >
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]);
    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 >
map< N > make_map(pair (&a)[N], std::index_sequence< indices... >)
    return {{a[indices]...}};

template< int N >
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 > \

#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 s{4, {'a', 'b', 'c'}, 'd'};
    assert((void *)&s.i == (void *)&s);
    assert((void *)&s.t < (void *)&s.c);
    static_assert(sizeof(s) == 8);


Some solutions:

  • Reorder the fields with clang-reorder-fields in clang-tools-extra
  • Let gcc do that automatically using -fipa-struct-reorg. Since that option has been removed, currently if you want to try you'll have to use the struct-reorg branch on gcc SVN or the github mirror


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


  • For MS compiler use #pragma pack, (#pragma pack(1) eliminates all gaps). And check the link for details, as this directive is not guaranteed to work always.
  • For GCC there is __attribute__ ((packed))

As your only goal is the minimal possible size of the data in memory, this is exactly what you need.