编译时检查以确保结构中的任何地方都没有填充
Compile-time check to make sure that there is no padding anywhere in a struct
有没有办法编写一个编译时断言来检查某些类型是否有任何填充?
例如:
struct This_Should_Succeed
{
int a;
int b;
int c;
};
struct This_Should_Fail
{
int a;
char b;
// because there are 3 bytes of padding here
int c;
};
编辑:选中 。
Is there a way to write a compile-time assertion that checks if some type has any padding in it?
是的。
您可以对所有成员的 sizeof 求和并将其与 class 本身的大小进行比较:
static_assert(sizeof(This_Should_Succeed) == sizeof(This_Should_Succeed::a)
+ sizeof(This_Should_Succeed::b)
+ sizeof(This_Should_Succeed::c));
static_assert(sizeof(This_Should_Fail) != sizeof(This_Should_Fail::a)
+ sizeof(This_Should_Fail::b)
+ sizeof(This_Should_Fail::c));
不幸的是,这需要为求和明确命名成员。自动解决方案需要(编译时)反射。不幸的是,C++ 语言还没有这样的特性。如果我们幸运的话,也许在 C++23 中。目前,有基于将 class 定义包装在宏中的解决方案。
一个不可移植的解决方案可能是使用 GCC 提供的 -Wpadded
选项,如果结构包含任何填充,它承诺会发出警告。这可以与 #pragma GCC diagnostic push
结合使用,只对选定的结构执行此操作。
type I'm checking, the type is a template input.
一种可移植但不完全令人满意的方法可能是使用自定义特征,模板的用户可以使用该特征来自愿承诺该类型不包含填充,从而使您可以利用知识。
用户将不得不依赖显式或基于预处理器的断言来证明他们的承诺是正确的。
从 C++17 开始,您可能 能够使用 std::has_unique_object_representations
.
#include <type_traits>
static_assert(std::has_unique_object_representations_v<This_Should_Succeed>); // succeeds
static_assert(std::has_unique_object_representations_v<This_Should_Fail>); // fails
尽管如此,这可能无法完全按照您的要求执行。查看链接的 cppreference 页面了解详细信息。
要在不重新键入每个结构成员的情况下获得总字段大小,您可以使用 X Macro
首先定义所有字段
#define LIST_OF_FIELDS_OF_This_Should_Fail \
X(int, a) \
X(char, b) \
X(int, c)
#define LIST_OF_FIELDS_OF_This_Should_Succeed \
X(long long, a) \
X(long long, b) \
X(int, c) \
X(int, d) \
X(int, e) \
X(int, f)
然后声明结构
struct This_Should_Fail {
#define X(type, name) type name;
LIST_OF_FIELDS_OF_This_Should_Fail
#undef X
};
struct This_Should_Succeed {
#define X(type, name) type name;
LIST_OF_FIELDS_OF_This_Should_Succeed
#undef X
};
并检查
#define X(type, name) sizeof(This_Should_Fail::name) +
static_assert(sizeof(This_Should_Fail) == LIST_OF_FIELDS_OF_This_Should_Fail 0);
#undef X
#define X(type, name) sizeof(This_Should_Succeed::name) +
static_assert(sizeof(This_Should_Succeed) == LIST_OF_FIELDS_OF_This_Should_Succeed 0);
#undef X
或者您可以重复使用相同的 X 宏来检查
#define X(type, name) sizeof(a.name) +
{
This_Should_Fail a;
static_assert(sizeof(This_Should_Fail) == LIST_OF_FIELDS_OF_This_Should_Fail 0);
}
{
This_Should_Succeed a;
static_assert(sizeof(This_Should_Succeed) == LIST_OF_FIELDS_OF_This_Should_Succeed 0);
}
#undef X
有关这方面的更多信息,您可以阅读 Real-world use of X-Macros
另一种 non-portable 解决方案是将结构的大小与 packed 版本与 #pragma pack
or __attribute__((packed))
. #pragma pack
is also supported by many other compilers like GCC or IBM XL
进行比较
#ifdef _MSC_VER
#define PACKED_STRUCT(declaration) __pragma(pack(push, 1)) declaration __pragma(pack(pop))
#else
#define PACKED_STRUCT(declaration) declaration __attribute((packed))
#endif
#define THIS_SHOULD_FAIL(name) struct name \
{ \
int a; \
char b; \
int c; \
}
PACKED_STRUCT(THIS_SHOULD_FAIL(This_Should_Fail_Packed));
THIS_SHOULD_FAIL(This_Should_Fail);
static_assert(sizeof(This_Should_Fail_Packed) == sizeof(This_Should_Fail));
见Force C++ structure to pack tightly. If you want to have an even more portable pack macro then try this
相关:
在 GCC and Clang 中有一个用于此目的的 -Wpadded
选项
-Wpadded
Warn if padding is included in a structure, either to align an element of the structure or to align the whole structure. Sometimes when this happens it is possible to rearrange the fields of the structure to reduce the padding and so make the structure smaller.
如果该结构位于您无法修改的 header 中,那么在 一些 情况下,它可以像这样解决以获得打包副本结构
#include "header.h"
// remove include guard to include the header again
#undef HEADER_H
// Get the packed versions
#define This_Should_Fail This_Should_Fail_Packed
#define This_Should_Succeed This_Should_Succeed_Packed
// We're including the header again, so it's quite dangerous and
// we need to do everything to prevent duplicated identifiers:
// rename them, or define some macros to remove possible parts
#define someFunc someFunc_deleted
// many parts are wrapped in SOME_CONDITION so this way
// we're preventing them from being redeclared
#define SOME_CONDITION 0
#pragma pack(push, 1)
#include "header.h"
#pragma pack(pop)
#undef This_Should_Fail
#undef This_Should_Succeed
static_assert(sizeof(This_Should_Fail_Packed) == sizeof(This_Should_Fail));
static_assert(sizeof(This_Should_Succeed_Packed) == sizeof(This_Should_Succeed ));
这不适用于使用 #pragma once
的 header 或一些在其他 header 中包含结构的结构
有没有办法编写一个编译时断言来检查某些类型是否有任何填充?
例如:
struct This_Should_Succeed
{
int a;
int b;
int c;
};
struct This_Should_Fail
{
int a;
char b;
// because there are 3 bytes of padding here
int c;
};
编辑:选中
Is there a way to write a compile-time assertion that checks if some type has any padding in it?
是的。
您可以对所有成员的 sizeof 求和并将其与 class 本身的大小进行比较:
static_assert(sizeof(This_Should_Succeed) == sizeof(This_Should_Succeed::a)
+ sizeof(This_Should_Succeed::b)
+ sizeof(This_Should_Succeed::c));
static_assert(sizeof(This_Should_Fail) != sizeof(This_Should_Fail::a)
+ sizeof(This_Should_Fail::b)
+ sizeof(This_Should_Fail::c));
不幸的是,这需要为求和明确命名成员。自动解决方案需要(编译时)反射。不幸的是,C++ 语言还没有这样的特性。如果我们幸运的话,也许在 C++23 中。目前,有基于将 class 定义包装在宏中的解决方案。
一个不可移植的解决方案可能是使用 GCC 提供的 -Wpadded
选项,如果结构包含任何填充,它承诺会发出警告。这可以与 #pragma GCC diagnostic push
结合使用,只对选定的结构执行此操作。
type I'm checking, the type is a template input.
一种可移植但不完全令人满意的方法可能是使用自定义特征,模板的用户可以使用该特征来自愿承诺该类型不包含填充,从而使您可以利用知识。
用户将不得不依赖显式或基于预处理器的断言来证明他们的承诺是正确的。
从 C++17 开始,您可能 能够使用 std::has_unique_object_representations
.
#include <type_traits>
static_assert(std::has_unique_object_representations_v<This_Should_Succeed>); // succeeds
static_assert(std::has_unique_object_representations_v<This_Should_Fail>); // fails
尽管如此,这可能无法完全按照您的要求执行。查看链接的 cppreference 页面了解详细信息。
要在不重新键入每个结构成员的情况下获得总字段大小,您可以使用 X Macro
首先定义所有字段
#define LIST_OF_FIELDS_OF_This_Should_Fail \
X(int, a) \
X(char, b) \
X(int, c)
#define LIST_OF_FIELDS_OF_This_Should_Succeed \
X(long long, a) \
X(long long, b) \
X(int, c) \
X(int, d) \
X(int, e) \
X(int, f)
然后声明结构
struct This_Should_Fail {
#define X(type, name) type name;
LIST_OF_FIELDS_OF_This_Should_Fail
#undef X
};
struct This_Should_Succeed {
#define X(type, name) type name;
LIST_OF_FIELDS_OF_This_Should_Succeed
#undef X
};
并检查
#define X(type, name) sizeof(This_Should_Fail::name) +
static_assert(sizeof(This_Should_Fail) == LIST_OF_FIELDS_OF_This_Should_Fail 0);
#undef X
#define X(type, name) sizeof(This_Should_Succeed::name) +
static_assert(sizeof(This_Should_Succeed) == LIST_OF_FIELDS_OF_This_Should_Succeed 0);
#undef X
或者您可以重复使用相同的 X 宏来检查
#define X(type, name) sizeof(a.name) +
{
This_Should_Fail a;
static_assert(sizeof(This_Should_Fail) == LIST_OF_FIELDS_OF_This_Should_Fail 0);
}
{
This_Should_Succeed a;
static_assert(sizeof(This_Should_Succeed) == LIST_OF_FIELDS_OF_This_Should_Succeed 0);
}
#undef X
有关这方面的更多信息,您可以阅读 Real-world use of X-Macros
另一种 non-portable 解决方案是将结构的大小与 packed 版本与 #pragma pack
or __attribute__((packed))
. #pragma pack
is also supported by many other compilers like GCC or IBM XL
#ifdef _MSC_VER
#define PACKED_STRUCT(declaration) __pragma(pack(push, 1)) declaration __pragma(pack(pop))
#else
#define PACKED_STRUCT(declaration) declaration __attribute((packed))
#endif
#define THIS_SHOULD_FAIL(name) struct name \
{ \
int a; \
char b; \
int c; \
}
PACKED_STRUCT(THIS_SHOULD_FAIL(This_Should_Fail_Packed));
THIS_SHOULD_FAIL(This_Should_Fail);
static_assert(sizeof(This_Should_Fail_Packed) == sizeof(This_Should_Fail));
见Force C++ structure to pack tightly. If you want to have an even more portable pack macro then try this
相关:
在 GCC and Clang 中有一个用于此目的的 -Wpadded
选项
-Wpadded
Warn if padding is included in a structure, either to align an element of the structure or to align the whole structure. Sometimes when this happens it is possible to rearrange the fields of the structure to reduce the padding and so make the structure smaller.
如果该结构位于您无法修改的 header 中,那么在 一些 情况下,它可以像这样解决以获得打包副本结构
#include "header.h"
// remove include guard to include the header again
#undef HEADER_H
// Get the packed versions
#define This_Should_Fail This_Should_Fail_Packed
#define This_Should_Succeed This_Should_Succeed_Packed
// We're including the header again, so it's quite dangerous and
// we need to do everything to prevent duplicated identifiers:
// rename them, or define some macros to remove possible parts
#define someFunc someFunc_deleted
// many parts are wrapped in SOME_CONDITION so this way
// we're preventing them from being redeclared
#define SOME_CONDITION 0
#pragma pack(push, 1)
#include "header.h"
#pragma pack(pop)
#undef This_Should_Fail
#undef This_Should_Succeed
static_assert(sizeof(This_Should_Fail_Packed) == sizeof(This_Should_Fail));
static_assert(sizeof(This_Should_Succeed_Packed) == sizeof(This_Should_Succeed ));
这不适用于使用 #pragma once
的 header 或一些在其他 header 中包含结构的结构