12

Is there a way (trait or so) to detect, if struct/class has some padding?

I don't need cross-platform or standardized solution, I need it for MSVC2013.

I can check it like

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);

But C++ doesn't allow (as far as I know) generate this non-invasive (without touching existing structs)

Ideally I would like to get working something like this

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{};

EDIT - Why do I need this?

I'am working with existing serialization system, which store some struct just taking void* to them (inside generic function) and store sizeof(T) number of bytes... Such binary file is not portable on platforms we are targeting, since different compilers are used, so there is no guarantee how is padding inserted. If I could statically detect all T which are structs with padding, I can force user to manually insert padding (some control padding e.g. not just random garbage) so there is no "random" padding. Another adventage is, when I diff two save files of same scenerio, they will look the same.

edit 2 the more I think about it, the more I realize I need cross-platform solution. We mainly develop on msvc2013 but our application is at final builded in msvc2012 and clang. But if I detected and get rid of all compiler-generated padding in msvc2013, there is no guarantee that other compiler doesn't insert padding... (so msvc2013 detection is not enough)

relaxxx
  • 7,026
  • 8
  • 36
  • 64
  • 4
    Why do you think you need this? – Lightness Races in Orbit Aug 14 '15 at 10:02
  • 1
    Padding behaves much like an unnamed member. Since you can't enumerate members, it's impossible to distinguish between normal members and those "unnamed members" acting as padding. – MSalters Aug 14 '15 at 10:10
  • You know you can specify MSVC padding behaviour using the "Struct Member Alignment" option in the Code Generation configuration page, right? – Jeroen Baert Aug 14 '15 at 10:11
  • @JeroenBaert: That's just one of the sources of the variation which he's trying to detect. As the example code shows, it can also be defined on a class-by-class basis. – MSalters Aug 14 '15 at 10:14
  • @LightnessRacesinOrbit see edit – relaxxx Aug 14 '15 at 12:43
  • @relaxxx: Typically one achieves this by either serialising in a padding-agnostic way (i.e. member-by-member), or enforcing zero-padding with compiler intrinsics. I don't see the value in knowing what the implementation-defined padding is: you want to get rid of it, not know it! – Lightness Races in Orbit Aug 14 '15 at 13:20
  • there are actually two things I'm talking about. zero-padding (padding present, but not random garbage but zeros) ale less important but "nice to have". Other thing is, to save/load chunks on application produced by different compilers, so `sizeof` on the same struct doesn't have to give the same results. or padding could be placed differently. So I want to force user to manually put padding where some compiler would. – relaxxx Aug 14 '15 at 13:27
  • Clang has `-Wpadded`, maybe it can be made useful for your case. – geza Nov 05 '18 at 14:53
  • 3
    in many cases you can use `std::has_unique_object_representations`. See [Compile-time check to make sure that there is no padding anywhere in a struct](https://stackoverflow.com/q/57829861/995714) – phuclv Sep 18 '19 at 02:05

3 Answers3

2

Do you need this information during run time? Because if you want to know it in build time I believe you can use static_assert to get this information.

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!");

If you need it during run time, you can try something like:

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

Also check this link from earlier post, maybe it will help.

Alex Lop.
  • 6,740
  • 1
  • 27
  • 44
  • 3
    Thank you, unfortunately I need generic solution, it is not possible to enumerate all struct members of all struct by hand – relaxxx Aug 14 '15 at 12:44
  • Like @relaxxx has said, I run in the same problem and I don't see a simple solution without enumerate all of the types independently. Do you know a more elegant and generic solution? – LXSoft Nov 05 '18 at 10:32
  • I think this should be transformed to static const bool has_padding = (sizeof(foo) != EXPECTED_FOO_SIZE); Or static const bool has_padding = !(sizeof(foo) == EXPECTED_FOO_SIZE); The padding will be present only if there is a difference in the size if is equal no padding. Not-ing the result or the test make the truly sense for the variable in upper instance. – LXSoft Nov 05 '18 at 13:50
0

Try out this macro :

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

And now, at runtime you can test :

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

And ofc, you can use static_assert if you want to get error at compile time.

MichaelCMS
  • 4,623
  • 2
  • 21
  • 28
  • Thank you, but I don't see how this will work without touching existing struct, I need `trait`. Please, see my question – relaxxx Aug 14 '15 at 12:47
  • well, you don't need to modify it's content, you don't need to change it's pack , you however need to declare it using the above macro. The structure is not touched. – MichaelCMS Aug 14 '15 at 12:55
  • By not touch, you mean you cannot modify the file where the structure is declared ? – MichaelCMS Aug 14 '15 at 12:56
  • Yes. I don't want to (and can't) change all structs in our codebase and force others to write structs like that, see http://www.cplusplus.com/reference/type_traits/ for examples – relaxxx Aug 14 '15 at 13:08
  • Then you can't do it. There's no way you can do this with TMP traits. – Lightness Races in Orbit Aug 14 '15 at 13:21
-1

May be you should try something like this:

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

I got:

sizeof struct = 8
sizeof items  = 5

I am on Ubuntu 14.04.

Richard Rublev
  • 6,495
  • 12
  • 58
  • 100
  • 1
    This does not take into account extra space that is not padding (like a vPtr), and requires enumerating the members by hand. – Quentin Aug 14 '15 at 10:08
  • Thank you, unfortunately I need generic solution, it is not possible to enumerate all struct members of all struct by hand – relaxxx Aug 14 '15 at 12:48