Approach to automatic field reordering in C-like structures

Is there a way to automatically change fields in C-like structures? I mean the use of such language functions as (preprocessor for C and C ++ and templates / type types / etc for C ++), which allow you to make the following macro (Boost.Fusion style for adapting 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, the approach should take into account the alignof values ​​(along with sizeof ) for the fields and, if possible, the #pragma pack current value.

I know about poor portability of the result, but only for local use.

Be sure to save the field names along with the corresponding types.

The goal is to reduce the overall size of the structure.

+5
source share
3 answers

I found a 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 rk < 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); } 
+2
source

Some solutions:

Connected:

+1
source
  • For MS compiler use #pragma pack , ( #pragma pack(1) removes all spaces). And check the link for details, as this directive is not always guaranteed.
  • There is __attribute__ ((packed)) for GCC

Since your only goal is the smallest possible data size in memory, this is exactly what you need.

0
source

All Articles