c++ 检测结构是否有填充

wkftcu5l  于 2023-03-20  发布在  其他
关注(0)|答案(4)|浏览(122)

是否有方法(trait或其他)检测struct/class是否有填充?
我不需要跨平台或标准化的解决方案,我需要它用于MSVC 2013。
我可以检查一下

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结构体,它们是带有填充的,我可以强制用户手动插入填充(一些控制填充,例如,不仅仅是随机垃圾),这样就没有“随机”填充。另一个优点是,当我比较两个相同场景的保存文件时,它们看起来是一样的。

edit 2我越想越觉得我需要跨平台的解决方案。我们主要是在msvc 2013上开发的,但是我们的应用程序最后是在msvc 2012和clang中构建的。但是如果我在msvc 2013中检测到并清除了所有编译器生成的填充,就不能保证其他编译器不会插入填充...(所以msvc 2013检测是不够的)

vptzau2j

vptzau2j1#

你在运行时需要这些信息吗?因为如果你想在构建时知道这些信息,我相信你可以使用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从早期的职位,也许它会有帮助。

3yhwsihp

3yhwsihp2#

尝试使用此宏:

#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");
}

和ofc,如果想在编译时得到错误,可以使用static_assert。

hsvhsicv

hsvhsicv3#

C++17中最接近的是std::has_unique_object_representations,它基本上是您建议的has_padding<T>结构的布尔逆,但它也不能用于浮点类型。
在我的代码库中,我使用了std::has_unique_object_representations_v<T> || std::is_same_v<T, float> || std::is_same_v<T, double>的组合来检查我需要什么,但是这不包括float是成员变量的情况。
您可能需要创建自己的has_padding实现结构,该结构具有服从std::has_unique_object_representations<T>的泛型实现,其中包含floatdouble成员的自定义类型可以在您手动检查之后添加专门化:

#include <type_traits>

template <typename T>
struct has_padding : std::conditional<std::has_unique_object_representations<T>::value || std::is_same<T, float>::value || std::is_same<T, double>::value,
                                      std::false_type,
                                      std::true_type>::type{};

struct MyType
{
    float a;
    float b;
};
// I've checked it manually and decided MyType is okay:
template<>
struct has_padding<MyType> : std::false_type{};

它并不完美,但据我所知,它是最接近于自动检查C++中的结构填充的。

w8rqjzmb

w8rqjzmb4#

也许你应该试试这样的东西:

#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。

相关问题