packed is cursed, please no. I would much rather see std::unaligned<Trivial>.

On 6 December 2023 00:44:50 GMT, Frederick Virchanza Gotham via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
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;
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals