2

I have a class A like this:

struct V {
    virtual void f() = 0;
};

struct A {
    int data;
};

struct B : public A , public V {
    void f() override {}
};

MSVC gives me sizeof(A) == 4 and sizeof(B) == 16 on a 64 bit build instead of 12 (sizeof(void*) + sizeof(A)) - so there is a 4 byte padding. Is there a way to get that padding? perhaps with some trait?

The reason I need this is to do an assert like this:

static_assert(sizeof(B) == sizeof(A) + std::is_polymorphic<camera>::value * sizeof(void*));

Meaning that I want to ensure that all data is in the base class, but B should be able to be polymorphic by inheriting from some interface... No new members shall be added to B but it should be allowed to be polymorphic. If I had 2 integers in A there would be 0 padding at the end of B...

onqtam
  • 4,356
  • 2
  • 28
  • 50
  • "f I had 2 integers in A there would be 0 padding at the end of B." - C++ makes no such guarantee. –  Jul 06 '17 at 13:45
  • 1
    There is no trait to get padding. Additionally, the location and the size of padding is not specified by the standard. Your need for that assert is suspicious and leads me to believe you are attempting to do something that is also not defined by the standard. Could you elaborate on your goals? – François Andrieux Jul 06 '17 at 13:45
  • You can calculate it yourself using `alignof(B)`. – Brian Rodriguez Jul 06 '17 at 13:45
  • @BrianRodriguez Alignment is not the only factor in determining padding. – François Andrieux Jul 06 '17 at 13:47
  • I see, then the answer to this question is no: https://stackoverflow.com/questions/5397447/struct-padding-in-c – Brian Rodriguez Jul 06 '17 at 13:48
  • My use case is to have all my data in the base class, for which there are serialization routines generated by some codegen. I want to enforce that the derived class adds NO members because they would be skipped from the serialization. I know this is not defined by the standard and that it is compiler dependent, but I need to figure out atleast some way (perhaps using ```alignof```?) to enforce this. – onqtam Jul 06 '17 at 13:49
  • Is there anything that guarantees that making an object polymorphic increases it's size by exactly `sizeof(void*)`? – François Andrieux Jul 06 '17 at 13:49
  • @onqtam You can only enforce this on a per-compiler basis, provided that the compiler provides extensions or guarantees that strictly define padding and class sizes. – François Andrieux Jul 06 '17 at 13:50
  • @FrançoisAndrieux There is no guarantee but I need to get this working on gcc/clang/msvc and they behave the same way. – onqtam Jul 06 '17 at 13:51
  • @onqtam Perhaps if you defined some prototypical class that derives from `A` without additional members you can then compare it's size with the size of the user's derived type. It may fail to detect derived classes that define few small members (as they would fit within the padding). For example, if you had 2 bytes of padding this would fail to detect the presence of an additional `char` member. However, this weakness exists for *any* solution that would attempt to check for the presence of members using only the size of the type. I would recommend documentation over trying to make this work. – François Andrieux Jul 06 '17 at 13:57
  • @onqtam: Right now they might. But if you make such assumptions, and one of the compilers changes, your code may not compile on the very next version of said compilers. – ShadowRanger Jul 06 '17 at 13:57
  • What if you make the data into one class which cannot be derived from, and have it as a member variable in classes that do stuff, maybe with inheritence; then you can make the data class "sealed" i.e. no able to be drived from. – doctorlove Jul 06 '17 at 14:02
  • @doctorlove all my logic that will operate on the data will be in the derived class. I wouldn't like having to access it through a member variable – onqtam Jul 06 '17 at 14:10
  • Why not? It's a bit more typing, but the alternatives seem over the top, and maybe not portable – doctorlove Jul 06 '17 at 14:11
  • @doctorlove that would resemble ```this->...``` for members... I'd rather drop the static assert altogether. – onqtam Jul 06 '17 at 14:52

1 Answers1

1

I found a solution which seems to work on GCC and clang, at least for the example you gave.

namespace detail {

template <typename T, int N>
struct add_padding : add_padding<T, N - 1> {
    char pad;
};

template <typename T>
struct add_padding<T, 0> : T {
};

} // namespace detail

template <typename T, int N = 1, bool = (sizeof(T) == sizeof(detail::add_padding<T, N>))>
struct padding_size {
    static constexpr int value = padding_size<T, N + 1>::value;
};

template <typename T, int N>
struct padding_size<T, N, false> {
    static constexpr int value = N - 1;
};

It breaks for padding that is not caused by inheriting from different-sized types, and it doesn't work with MSVC at all. So it's not really an answer to your question I'm afraid, but maybe it helps someone else. Here's a live example.

Pezo
  • 1,458
  • 9
  • 15