C++ Logo

std-proposals

Advanced search

[std-proposals] [[packed]]

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Wed, 6 Dec 2023 00:44:50 +0000
There has been talk lately on the mailing list here of tail padding in
structs, and whether it can be either eradicated, or used for
overlapping objects.

Consider the following code:

    struct Monkey {
        long double a;
        int b;
    } __attribute__((packed));

    struct Donkey {
        long double a;
        int b;
    };

    int main(void)
    {
        static_assert( sizeof(Monkey) != sizeof(Donkey) );

        Monkey m;

        m.b = 6;

        int *p = &m.b;

        *p = 7;
    }

When this program is compiled with GNU g++ or LLVM clang++, it gives
the following diagnostic:

    warning: taking address of packed member 'b' of class
    or structure 'Monkey' may result in an unaligned pointer
    value [-Waddress-of-packed-member]

I was thinking . . . If [[packed]] were ever to be standardised, then
a 'packed struct' could behave as follows:
(Rule 1) Assignment is achieved by 'memcpy'. For example the line "m.b
= 6;" in the above snippet would become:
        int const six = 6;
        memcpy( &m.b, &six, sizeof m.b );
(Rule 2) When taking the address of any member of a packed struct, you
get a 'void*', for example:
        int *p = &m.b; /* fails to compile */
        void *p = &m.b; /* compiles okay */
(Rule 3) You cannot bind a reference to any member of a packed struct:
        int &b = m.b; /* fails to compile */

Of course you can get around Rule 2 and Rule 3 by using 'static_cast',
for example:
    int *p = static_cast<int*>(&m.b);
    int &b = *static_cast<int*>(&m.b);
but you only have to glance at these two lines to see that alignment
might be an issue.

An alternative to Rule No. 2 would be that you can't take the address
of any member of a packed struct -- instead you must use
'std::addressof_packed', so:

        int *p = &m.b; /* fails to compile */
        void *p = &m.b; /* fails to compile */
        int *p = std::addressof_packed(m.b); /* fails to compile */
        void *p = std::addressof_packed(m.b); /* compiles okay */

Furthermore . . . maybe we can create a new type, a 'packed struct',
from an already-defined 'unpacked struct'. So let's say we start out
with:

    struct Donkey {
        long double a;
        int b;
    };

And then later we do:

    typedef Donkey [[packed]] Monkey;

or alternatively:

    using Monkey = [[packed]] Donkey;

Received on 2023-12-06 00:45:04