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.
A Standard C compiler won't rearrange the members of a structure automatically to reduce the structure's size.
Since the rules are fixed in the language, the compiler is able to figure out how the members were reordered, and react accordingly. As mentioned above, it will always be possible to prevent reordering in the cases where you want complete control.
The order of fields in a struct does matter - the compiler is not allowed to reorder fields, so the size of the struct may change as the result of adding some padding.
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);
}
Some solutions:
clang-reorder-fields
in clang-tools-extra
-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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With