7

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):

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;

};

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.

Community
  • 1
  • 1
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
  • I think sizes of types is unknown things on preprocessor stage. Therefore positions of the names cannot be exchanged. Seems, I should wait for introspection introducing in the language. – Tomilov Anatoliy Mar 18 '15 at 11:58
  • You could achieve this with variadic templates and tuples, but you would be able to access the members only by index/type. – Vittorio Romeo Mar 18 '15 at 16:08
  • @Orient You can achieve the desired result by creating a code generator that takes in a configuration file and outputs the appropriate header files. – Coder Jul 23 '15 at 00:13

3 Answers3

3

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 >
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 s{4, {'a', 'b', 'c'}, 'd'};
    assert((void *)&s.i == (void *)&s);
    assert((void *)&s.t < (void *)&s.c);
    static_assert(sizeof(s) == 8);
}
Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169
2

Some solutions:

  • Reorder the fields with clang-reorder-fields in clang-tools-extra
  • Let gcc do that automatically using -fipa-struct-reorg. That option has been removed since gcc 4.8.x but is still in active development for inclusion back to official gcc in a future version. In the meantime if you want to try you'll have to build gcc from source using the struct-reorg branch on gcc SVN or the github mirror

Related:

phuclv
  • 37,963
  • 15
  • 156
  • 475
0
  • 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.

Community
  • 1
  • 1
avp
  • 4,895
  • 4
  • 28
  • 40