【问题标题】:Detect if struct has padding检测结构是否有填充
【发布时间】:2015-11-07 12:57:47
【问题描述】:

如果结构/类有一些填充,有没有办法(特征左右)来检测?

我不需要跨平台或标准化的解决方案,我在 MSVC2013 中需要它。

我可以检查一下

namespace A
{
    struct Foo
    {
        int a;
        bool b;
    };
}

#pragma pack(push, 1)
namespace B
{
    struct Foo
    {
        int a;
        bool b;
    };
}
#pragma pack(pop)

static const bool has_padding = sizeof(A::Foo) != sizeof(B::Foo);

但 C++ 不允许(据我所知)生成这种非侵入式(不触及现有结构)

理想情况下,我想从事这样的工作

template <typename T>
struct has_padding_impl
{
    typedef __declspec(align(1)) struct T AllignedT;
};

template <typename T>
struct has_padding : typename std::conditional<sizeof(typename has_padding_impl<T>::AllignedT) == sizeof(T),
                                               std::false_type,
                                               std::true_type>::type{};

编辑 - 我为什么需要这个?

我正在使用现有的序列化系统,它存储了一些结构,只是将void* 传递给它们(在通用函数内部)并存储sizeof(T) 字节数......这样的二进制文件在我们的目标平台上是不可移植的, 由于使用了不同的编译器,因此无法保证如何插入填充。如果我可以静态检测所有带有填充的结构 T,我可以强制用户手动插入填充(一些控制填充,例如不仅仅是随机垃圾),因此没有“随机”填充。另一个冒险是,当我比较相同场景的两个保存文件时,它们看起来是一样的。

编辑 2 我想得越多,我就越意识到我需要跨平台的解决方案。我们主要在 msvc2013 上开发,但我们的应用程序最终构建在 msvc2012 和 clang 上。但是如果我在 msvc2013 中检测到并去掉所有编译器生成的填充,则不能保证其他编译器不会插入填充...(所以 msvc2013 检测是不够的)

【问题讨论】:

  • 你认为你为什么需要这个?
  • Padding 的行为很像一个未命名的成员。由于无法枚举成员,因此无法区分普通成员和充当填充的“未命名成员”。
  • 您知道可以使用代码生成配置页面中的“结构成员对齐”选项指定 MSVC 填充行为,对吧?
  • @JeroenBaert:这只是他试图检测的变异来源之一。如示例代码所示,也可以逐类定义。
  • 在很多情况下你可以使用std::has_unique_object_representations。见Compile-time check to make sure that there is no padding anywhere in a struct

标签: c++ visual-c++ padding typetraits


【解决方案1】:

您在运行时需要这些信息吗? 因为如果您想在构建时了解它,我相信您可以使用static_assert 获取此信息。

struct foo
{
    uint64_t x;
    uint8_t y;
};
#define EXPECTED_FOO_SIZE (sizeof(uint64_t) + sizeof(uint8_t))
static_assert(sizeof(foo) == EXPECTED_FOO_SIZE, "Using padding!");

如果您在运行时需要它,您可以尝试以下方法:

static const bool has_padding = (sizeof(foo) != EXPECTED_FOO_SIZE);

还可以查看之前帖子中的link,也许会有所帮助。

【讨论】:

  • 谢谢,很遗憾我需要通用解决方案,无法手动枚举所有结构的所有结构成员
  • 就像@relaxxx 所说,我遇到了同样的问题,如果不独立枚举所有类型,我看不到一个简单的解决方案。你知道更优雅和通用的解决方案吗?
  • 我认为这应该转换为 static const bool has_padding = (sizeof(foo) != EXPECTED_FOO_SIZE);或 static const bool has_padding = !(sizeof(foo) == EXPECTED_FOO_SIZE);只有当大小有差异时才会出现填充,如果等于没有填充。注意结果或测试对上例中的变量真正有意义。
【解决方案2】:

试试这个宏:

#define TO_STR(str) #str
#define DECL_STRUCT_TEST_ALIGNED(structName, test_alignment, body) \
_Pragma(TO_STR(pack(push,test_alignment)))\
struct test_##structName \
body ; \
_Pragma(TO_STR(pack(pop))) \
struct structName \
body; \
static const bool has_padding_##structName = sizeof(test_##structName)!=sizeof(structName);

DECL_STRUCT_TEST_ALIGNED(bar, 1,
{
                         int a;
                         bool b;
                     }
                     )


DECL_STRUCT_TEST_ALIGNED(foo,1,
{
                         int a;
                         int b;
                     })

现在,您可以在运行时进行测试:

if (has_padding_foo)
{
    printf("foo has padding\n");
} else {
    printf("foo doesn't have padding\n");
}
if (has_padding_bar)
{
    printf("bar has padding\n");
} else {
    printf("bar has no padding\n");
}

而且,如果你想在编译时出错,你可以使用 static_assert。

【讨论】:

  • 谢谢,但如果不触及现有结构,我看不出这将如何工作,我需要trait。请看我的问题
  • 嗯,你不需要修改它的内容,你不需要改变它的 pack ,但是你需要使用上面的宏来声明它。结构没有被触及。
  • 不碰是指不能修改声明结构的文件?
  • 是的。我不想(也不能)更改我们代码库中的所有结构并强迫其他人编写这样的结构,请参阅cplusplus.com/reference/type_traits 示例
  • 那你就不行了。使用 TMP 特征无法做到这一点。
【解决方案3】:

也许你应该尝试这样的事情:

#include <iostream>
using namespace std;

struct A
{
    int a;
    bool b;
};

int main(int argc, char *argv[])

{
    A foo;

    cout << "sizeof struct = " << sizeof(A) << endl;
    cout << "sizeof items  = " << sizeof(foo.a) + sizeof(foo.b) << endl;
    return 0;
}

我明白了:

sizeof struct = 8
sizeof items  = 5

我在 Ubuntu 14.04 上。

【讨论】:

  • 这不考虑没有填充的额外空间(如 vPtr),并且需要手动枚举成员。
  • 谢谢,很遗憾我需要通用解决方案,无法手动枚举所有结构的所有结构成员
猜你喜欢
  • 2019-06-20
  • 2011-08-13
  • 2011-09-08
  • 2021-11-22
  • 2016-10-05
  • 2021-07-28
  • 2017-11-07
  • 2015-04-27
相关资源
最近更新 更多