Date: Mon, 19 Aug 2019 15:52:35 -0500
On Mon, Aug 19, 2019 at 3:16 PM language.lawyer--- via Std-Proposals <
std-proposals_at_[hidden]> wrote:
> On 19/08/2019 22:57, Brian Bi wrote:
> > On Mon, Aug 19, 2019 at 2:15 PM <language.lawyer_at_[hidden]> wrote:
> >
> >> On 19/08/2019 19:33, Brian Bi wrote:
> >>> On Sat, Aug 17, 2019 at 2:16 PM Language Lawyer via Std-Proposals <
> >>> std-proposals_at_[hidden]> wrote:
> >>>
> >>>> std::bless<std::byte[sizeof(T)]> (or how it is currently named), when
> it
> >>>> is added to the standard, will kinda allow accessing object
> >> representation
> >>>> without introducing very non-trivial changes to the C++ memory and
> >> object
> >>>> model.
> >>>
> >>> If I understand the std::bless approach properly, it has a serious
> >>> shortcoming: let's say T has some sufficiently nontrivial class type.
> >> Then,
> >>> the call to std::bless followed by reading from the std::byte array
> will
> >>> create the std::byte array, which *ends the lifetime of the *T since
> the
> >> T's
> >>> storage has now been reused by the std::byte array. If T is not an
> >>> implicit-lifetime type, then that means if you still want to use the
> >>> original T value *qua* T, then tough luck. If the T object had
> automatic
> >>> storage duration and a nontrivial destructor, then UB will happen when
> it
> >>> goes out of scope.
> >>
> >> Right, you'll need to recreate an object of type T using std::bless<T>
> >> when you're done accessing the array.
> >> (Note that you'll have to do this even if T is an implicit-lifetime
> type!)
> >>
> >
> > I think if T is an implicit-lifetime type, the std::bless<std::byte[N]>
> > call can implicitly create the array first, and then implicitly create
> the T
> > object as well (so the array provides storage for the T object, and both
> > now coexist).
>
> The creation of an object of type T would end the lifetime of the array
> elements, so accessing them would be UB.
>
An array of unsigned char or std::byte can have an object of some other
type nested within it, so the array's lifetime is not ended.
>
> > But if T is not an implicit-lifetime type, then calling std::bless<T>
> > afterward won't work.
>
> I should have said this in the beginning: I don't think accessing the
> representation of objects of not implicit-lifetime type makes sense (maybe
> T should be even more constrained than implicit-lifetime type).
> So this is not an issue.
> > Plus, even if T *is* an implicit-lifetime type, the idea that you have to
> > destroy and recreate the object in order to observe its object
> > representation seems untenable. What happens if T contains a const
> member?
> > Now everyone who had access to that T object has to launder their
> pointers,
> > just because someone wanted to observe the object representation.
>
> This just shows that the access to object representations do not fit well
> into the current model of the C++ abstract machine.
>
Indeed. On one hand, one could conclude that people who want to access
object representations should just suck it up. On the other hand, one could
observe that the current C++ memory and object model results in a lot of
pre-C++17 code being broken, and conclude that it should be revised.
Besides, even if you think that no one should ever try to access object
representations of anything much more complicated than a struct with a
bunch of ints in it, do you think it is reasonable to limit typical usage
of offsetof to such types as well? If I have a pointer to a member of some
class that is standard-layout but not implicit-lifetime, should there not
be a way for me to get a pointer to the enclosing class using pointer
arithmetic? Are you comfortable with letting all the existing code of this
form, written before C++17, continue to be broken? What did we gain from
the C++17 changes that was so beneficial that it justified *silently
breaking* said code? What about the fact that the migration effort for such
code can be significant (given, as I've pointed out, the inadequacies of
the proposed std::bless solution)?
>
> >>
> >> I do not think that this is a good way.
> >>
> >
> > What problem did this cause when it was effectively the status quo in
> > C++14, that is so serious that we should not attempt to codify it in
> C++23
> > or some future version of the standard?
>
> I do not think calling bytes of memory "objects" is a good status quo.
>
Is that really your only objection? Presumably, it would be possible to
word the changes to the standard in a way that would avoid this. The point
is that existing code relies on being able to do pointer arithmetic on
memory that various types of objects occupy *as though* an array of unsigned
char objects existed there. We don't have to say that there are actual
objects. Given the way pointers work in C++17, I think this would require a
lot of wording, but I think I've already stated enough times how the
alternative of doing nothing leaves a lot of code broken.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
std-proposals_at_[hidden]> wrote:
> On 19/08/2019 22:57, Brian Bi wrote:
> > On Mon, Aug 19, 2019 at 2:15 PM <language.lawyer_at_[hidden]> wrote:
> >
> >> On 19/08/2019 19:33, Brian Bi wrote:
> >>> On Sat, Aug 17, 2019 at 2:16 PM Language Lawyer via Std-Proposals <
> >>> std-proposals_at_[hidden]> wrote:
> >>>
> >>>> std::bless<std::byte[sizeof(T)]> (or how it is currently named), when
> it
> >>>> is added to the standard, will kinda allow accessing object
> >> representation
> >>>> without introducing very non-trivial changes to the C++ memory and
> >> object
> >>>> model.
> >>>
> >>> If I understand the std::bless approach properly, it has a serious
> >>> shortcoming: let's say T has some sufficiently nontrivial class type.
> >> Then,
> >>> the call to std::bless followed by reading from the std::byte array
> will
> >>> create the std::byte array, which *ends the lifetime of the *T since
> the
> >> T's
> >>> storage has now been reused by the std::byte array. If T is not an
> >>> implicit-lifetime type, then that means if you still want to use the
> >>> original T value *qua* T, then tough luck. If the T object had
> automatic
> >>> storage duration and a nontrivial destructor, then UB will happen when
> it
> >>> goes out of scope.
> >>
> >> Right, you'll need to recreate an object of type T using std::bless<T>
> >> when you're done accessing the array.
> >> (Note that you'll have to do this even if T is an implicit-lifetime
> type!)
> >>
> >
> > I think if T is an implicit-lifetime type, the std::bless<std::byte[N]>
> > call can implicitly create the array first, and then implicitly create
> the T
> > object as well (so the array provides storage for the T object, and both
> > now coexist).
>
> The creation of an object of type T would end the lifetime of the array
> elements, so accessing them would be UB.
>
An array of unsigned char or std::byte can have an object of some other
type nested within it, so the array's lifetime is not ended.
>
> > But if T is not an implicit-lifetime type, then calling std::bless<T>
> > afterward won't work.
>
> I should have said this in the beginning: I don't think accessing the
> representation of objects of not implicit-lifetime type makes sense (maybe
> T should be even more constrained than implicit-lifetime type).
> So this is not an issue.
> > Plus, even if T *is* an implicit-lifetime type, the idea that you have to
> > destroy and recreate the object in order to observe its object
> > representation seems untenable. What happens if T contains a const
> member?
> > Now everyone who had access to that T object has to launder their
> pointers,
> > just because someone wanted to observe the object representation.
>
> This just shows that the access to object representations do not fit well
> into the current model of the C++ abstract machine.
>
Indeed. On one hand, one could conclude that people who want to access
object representations should just suck it up. On the other hand, one could
observe that the current C++ memory and object model results in a lot of
pre-C++17 code being broken, and conclude that it should be revised.
Besides, even if you think that no one should ever try to access object
representations of anything much more complicated than a struct with a
bunch of ints in it, do you think it is reasonable to limit typical usage
of offsetof to such types as well? If I have a pointer to a member of some
class that is standard-layout but not implicit-lifetime, should there not
be a way for me to get a pointer to the enclosing class using pointer
arithmetic? Are you comfortable with letting all the existing code of this
form, written before C++17, continue to be broken? What did we gain from
the C++17 changes that was so beneficial that it justified *silently
breaking* said code? What about the fact that the migration effort for such
code can be significant (given, as I've pointed out, the inadequacies of
the proposed std::bless solution)?
>
> >>
> >> I do not think that this is a good way.
> >>
> >
> > What problem did this cause when it was effectively the status quo in
> > C++14, that is so serious that we should not attempt to codify it in
> C++23
> > or some future version of the standard?
>
> I do not think calling bytes of memory "objects" is a good status quo.
>
Is that really your only objection? Presumably, it would be possible to
word the changes to the standard in a way that would avoid this. The point
is that existing code relies on being able to do pointer arithmetic on
memory that various types of objects occupy *as though* an array of unsigned
char objects existed there. We don't have to say that there are actual
objects. Given the way pointers work in C++17, I think this would require a
lot of wording, but I think I've already stated enough times how the
alternative of doing nothing leaves a lot of code broken.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
-- *Brian Bi*
Received on 2019-08-19 15:54:51