C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [[packed]] std::unaligned

From: connor horman <chorman64_at_[hidden]>
Date: Mon, 11 Dec 2023 22:57:42 -0500
The proposed assignment operator has undefined behaviour, because you're
referring to an object of type `T` in storage which is not the appropriate
size for `T`. `std::memcpy` cannot create an object of type `T` in that
storage, because an object of type `T` can only exist in storage that has
size and alignment `sizeof(T)` and `alignof(T)`. `__datasizeof(T)` I
believe excludes tail padding, but the compile may make use of the full
`sizeof(T)`. `unaligned<T>` could certainly internally contain only
`__datasizeof(T)` bytes, but any time you copy out `T`

Also, trivially_relocatable is further incorrect, because even if you did
use the correct size, implicit creation of objects requires that `T` be an
implicit lifetime type. Even if the storage was appropriately sized, a
trivially relocatable T would not necessarily be an implicit lifetime type.
`std::unique_ptr` would be a counter example - it is not an aggregate type,
and has neither a trivial eligible constructor nor a trivial destructor.
While the definition is certainly more expansive than trivially-copyable,
I'm not sure it is meaningfully more expansive, given the special member
functions all requiring that the underlying one from `T` is trivial.





On Mon, 11 Dec 2023 at 21:14, Thiago Macieira via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Monday, 11 December 2023 20:25:19 EST Frederick Virchanza Gotham via
> Std-
> Proposals wrote:
> > struct static_string {
> > char buf[256u];
> > char *plast;
> > };
> >
> > And so if the string is 10 characters long, then 'plast' will be equal
> > to 'this + 10 - 1'. The class is no longer trivially relocatable.
>
> Strictly as above, no, it's still trivially relocatable. You need to add
> the
> copy & move constructors to make it no longer trivially-relocatable. I
> assume
> you omitted them for brevity.
>
> > Arthur you said in your last post that I "never relocate-out-of (nor
> > into) a `T`", but if you look at my implementation of the assignment
> > operator (which I implemented as a template function), I do a memcpy
> > to a suitably-aligned T-sized buffer and then perform an operation on
> > it -- it is for this reason that I need T to be trivially relocatable.
>
> That's not a trivial relocation. That's a trivial *copy*. The original
> object
> is still there. It makes sense to add the ability to relocate out of a
> std::unaligned. Semantically, it does not make sense to impose the
> requirement
> on the contained type.
>
> That said, since the type must be trivially copyable and movable, by
> construction it will also be trivially relocatable. The three trivial
> operations are the same: a memcpy.
>
> > Anyway I've added a load of binary operators and unary operators to my
> > previous code (adapted from Connor's code), and here's what I've got
> > now:
> >
> > https://godbolt.org/z/z7MGMnzre
>
> I think that's an unnecessary distraction for now. std::atomic has them,
> but
> let's focus on the basics first.
>
> > But even after we put together a robust implementation of
> > std::unaligned (and I think we're getting close to it), I still want
> > to be able to do:
> >
> > struct TokenInfo {
> > long unsigned a;
> > char b;
> > long unsigned c;
> > char d;
> > };
> >
> > typedef struct TokenInfo [[packed]] TicketInfo;
>
> I think that's a bad idea. This is a completely distinct type than the
> original TokenInfo. It may make sense to have a std::deep_unaligned, which
> would need to either wait for reflection or would require compiler
> support. At
> which point it may be a keyword instead of a class or an attribute.
>
> > Furthermore, I think that 'unaligned' should be compatible with
> > 'optional'
>
> It is compatible.
>
> > so that you can do the following:
> >
> > std::optional<SomeClass> monkey;
> > monkey.emplace(1,2,3,56.7L);
> > std::unaligned<SomeClass> donkey( std::take_ownership_t(), monkey );
>
> There's no such thing as std::take_ownership. You'll need to describe the
> intent here, as we're not very good at diving it. What does it return? And
> if
> it's not a SomeClass, why would std::unaligned have a constructor that can
> receive it?
>
> > Therefore the destructor of 'unaligned' would have to
> > do something like:
> >
> > ~unaligned(void)
> > {
> > alignas(T) std::byte buf[ __datasizeof(T) ];
> > T &tmp = *static_cast<T*>(static_cast<void*>(buf));
> > std::memcpy(&tmp, &_m_data.front(), __datasizeof(T));
> > tmp.~T();
> > }
>
> unalgined's destructor ought to be trivial. So the only implementation is
>
> = default
>
> Even if not, your suggestion is not acceptable. Either the contained type
> has
> a trivial destructor (as discussed before), in which case this is a no-op
> and
> thus wholly unnecessary, or it isn't trivial and in which case the address
> may
> matter. If it does matter, then the suggested destructor will cause
> misbehaviour.
>
If the implicit-lifetime type issue does not apply, then a
trivially_relocatable type would by definition not have misbehaviour after
you move the type into `unaligned`.

>
> > Also another thing... I would want the 'unaligned' class to be derived
> > from a base class such as:
>
> Can't do it, because the base sub-object can't exist inside the derived
> object, given the alignment constraints.
>
> --
> Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
> Software Architect - Intel DCAI Cloud Engineering
>
>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2023-12-12 03:57:58