类 C 结构中自动字段重新排序的方法
Approach for automatic fields reordering in C-like structs
有没有办法在类似 C 的结构中执行 automatic fields reordering?我的意思是使用语言特性(C 和 C++ 的预处理器和 C++ 的 templates/type traits/etc),这使得可以执行以下宏(Boost.Fusion-like 风格来适应结构):
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
当前值。
我知道结果的可移植性不好,但它仅供本地使用。
必须将字段名称与各自的类型一起保存。
目的是减少总体结构大小。
- 对于 MS 编译器,使用 #pragma pack,(
#pragma pack(1)
消除了所有间隙)。并查看 link 了解详细信息,因为此指令不能保证始终有效。
- 对于 GCC 有
__attribute__ ((packed))
由于您的唯一目标是尽可能减少内存中数据的大小,因此这正是您所需要的。
我找到了 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 s{4, {'a', 'b', 'c'}, 'd'};
assert((void *)&s.i == (void *)&s);
assert((void *)&s.t < (void *)&s.c);
static_assert(sizeof(s) == 8);
}
一些解决方案:
- 使用
clang-reorder-fields
in clang-tools-extra
重新排序字段
- 让 gcc 使用
-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 自动执行此操作
相关:
- Automated field re-ordering in C structs to avoid padding
有没有办法在类似 C 的结构中执行 automatic fields reordering?我的意思是使用语言特性(C 和 C++ 的预处理器和 C++ 的 templates/type traits/etc),这使得可以执行以下宏(Boost.Fusion-like 风格来适应结构):
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
当前值。
我知道结果的可移植性不好,但它仅供本地使用。
必须将字段名称与各自的类型一起保存。
目的是减少总体结构大小。
- 对于 MS 编译器,使用 #pragma pack,(
#pragma pack(1)
消除了所有间隙)。并查看 link 了解详细信息,因为此指令不能保证始终有效。 - 对于 GCC 有
__attribute__ ((packed))
由于您的唯一目标是尽可能减少内存中数据的大小,因此这正是您所需要的。
我找到了 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 s{4, {'a', 'b', 'c'}, 'd'};
assert((void *)&s.i == (void *)&s);
assert((void *)&s.t < (void *)&s.c);
static_assert(sizeof(s) == 8);
}
一些解决方案:
- 使用
clang-reorder-fields
inclang-tools-extra
重新排序字段
- 让 gcc 使用
-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 thestruct-reorg
branch on gcc SVN or the github mirror 自动执行此操作
相关:
- Automated field re-ordering in C structs to avoid padding