Discussion:
Comitee stance on using aligned char arrays as raw storage without placement new for trivial types?
(too old to reply)
Martin Ba
2017-01-15 20:56:56 UTC
Permalink
Hi.

I'm currently trying to understand a few ... interesting ... observations I
have been making wrt. the C++ Standard and using char arrays as raw storage.

Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.

See:
http://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array
or related questions where I'm told I'm expected to do the following:

alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract machine!
::new (buf) int; // is this strictly required? (aside: it's obviously a no-op)

// access storage:
*((int*)buf) = 42; // for this discussion, just assume the cast itself yields the correct pointer value}

Now, I'm **not** asking whether the current C++ Standard requires - or not - the noop placement new for this code to be defined.


*What I would be interested in is whether this has been discussed in the committee (CWG?) in the last very few yearsand whether there is any agreement if omitting the placement new (for trivial type) should be allowed or if Standard C++ should absolutely require the placement new.*

Simple links to any paper(s) discussing this would be already appreciated, the only reference I found was P0137R1, and that's more about clarifying current wording afaikt.

Thanks.

- Martin

p.s.: (*) is "trivial type" the correct term?

p.p.s.: My personal impression on the matter is that requiring the placement new for trivial types (like int, ...) is rather insane and the amount of real world code compiled with C++ compilers
that would be broken should any C++ compiler/optimizer ever manage to actually treat this as UB is quite huge. 'Course I may be totally off here. Just take this as a disclaimer :-)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-01-15 23:37:00 UTC
Permalink
I cannot tell you if any discussion has been had. However, I can give you
some of the history of what in C++ prevents merely allocating memory from
creating an object. That in itself is not definitive about discussions, but
it is suggestive.

I do not actually have a copy of C++98 or C++03. The oldest working draft I
can find that is available is N1638, which was released in 2004.
An object is created by a definition (3.1), by a new-expression (5.3.4)
or by the implementation (12.2) when needed.

I do have late drafts of C++11 and C++14. I'm not going to quote from their
version of this section because they all say *the exact same thing*.

N4616, the current working draft leading into C++17, however, does change
An object is created by a definition (3.1), by a new-expression (5.3.4),
when implicitly changing the active member of a union (9.3), or when a
temporary object is created (4.4, 12.2).

So the only change has been essentially a defect fix that makes unions
actually work, in accord with the standard.

In at least 12 years of standardization, the committee has made no
substantive change to the causes of bringing an object into being. While
this is not conclusive, the fact that C++17 did put a fix into this section
means that they have looked at it and talked about it at some point. So I
would suggest that, if there was discussion about it, it did not progress
beyond discussion.
Hi.
I'm currently trying to understand a few ... interesting ... observations
I have been making wrt. the C++ Standard and using char arrays as raw
storage.
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
http://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array
alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract machine!
::new (buf) int; // is this strictly required? (aside: it's obviously a no-op)
*((int*)buf) = 42; // for this discussion, just assume the cast itself yields the correct pointer value}
Now, I'm **not** asking whether the current C++ Standard requires - or not - the noop placement new for this code to be defined.
*What I would be interested in is whether this has been discussed in the committee (CWG?) in the last very few yearsand whether there is any agreement if omitting the placement new (for trivial type) should be allowed or if Standard C++ should absolutely require the placement new.*
Simple links to any paper(s) discussing this would be already appreciated, the only reference I found was P0137R1, and that's more about clarifying current wording afaikt.
Thanks.
- Martin
p.s.: (*) is "trivial type" the correct term?
Correct term for what? Trivial Type is *a term* in C++, but it's unclear
what it would mean for what you want to do.

Conceptually, a TrivialType is a type which is a pure block-of-bits, one
for which any value of those bits is no less legal than any other. But C++
has other kinds of types.

A TriviallyCopyable type is a type for which a byte-by-byte copy operation
is equivalent to a language-level copy or move operation. A
TriviallyDefaultConstructible type is a type for which being uninitialized
is a legitimate state. A TriviallyDestructible type is a type whose
destruction is essentially irrelevant and can be ignored.

p.p.s.: My personal impression on the matter is that requiring the placement new for trivial types (like int, ...) is rather insane and the amount of real world code compiled with C++ compilers
that would be broken should any C++ compiler/optimizer ever manage to actually treat this as UB is quite huge. 'Course I may be totally off here. Just take this as a disclaimer :-)
Compilers *do* treat it as UB. UB doesn't mean "crash"; UB can still do
what you want.

The point of the UB designation is to allow implementations to be
reasonably fast. If you reinterpret cast a pointer to a different type, the
compiler doesn't have to check to see if that object really exists there;
it will simply trust your cast and pretend that there is an object there.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Martin Ba
2017-01-16 19:08:38 UTC
Permalink
I cannot tell you if any discussion has been had. However, ...
... So the only change has been essentially a defect fix that makes unions
actually work, in accord with the standard.
In at least 12 years of standardization, the committee has made no
substantive change to the causes of bringing an object into being. While
this is not conclusive, the fact that C++17 did put a fix into this section
means that they have looked at it and talked about it at some point. So I
would suggest that, if there was discussion about it, it did not progress
beyond discussion.
Thanks a lot for that wrap up!
-snip-
Post by Martin Ba
p.p.s.: My personal impression on the matter is that requiring the placement new for trivial types (like int, ...) is rather insane and the amount of real world code compiled with C++ compilers
that would be broken should any C++ compiler/optimizer ever manage to actually treat this as UB is quite huge. 'Course I may be totally off here. Just take this as a disclaimer :-)
Compilers *do* treat it as UB. UB doesn't mean "crash"; UB can still do
what you want.
The point of the UB designation is to allow implementations to be
reasonably fast. If you reinterpret cast a pointer to a different type, the
compiler doesn't have to check to see if that object really exists there;
it will simply trust your cast and pretend that there is an object there.
What I meant by "treating it as UB" was in the same vein as, e.g., signed
integer overflow. Compilers generate code today that doesn't work anymore
if it relies/relied on signed integer overflow, although older optimizer
didn't "break" anything.

In the same vein, I'm sure we can imagine several transformations that
break code that has no "placement new" (from my OP) that used (and uses) to
work.

- Martin
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-01-16 20:29:02 UTC
Permalink
Post by Martin Ba
I cannot tell you if any discussion has been had. However, ...
... So the only change has been essentially a defect fix that makes
unions actually work, in accord with the standard.
In at least 12 years of standardization, the committee has made no
substantive change to the causes of bringing an object into being. While
this is not conclusive, the fact that C++17 did put a fix into this section
means that they have looked at it and talked about it at some point. So I
would suggest that, if there was discussion about it, it did not progress
beyond discussion.
Thanks a lot for that wrap up!
-snip-
Post by Martin Ba
p.p.s.: My personal impression on the matter is that requiring the placement new for trivial types (like int, ...) is rather insane and the amount of real world code compiled with C++ compilers
that would be broken should any C++ compiler/optimizer ever manage to actually treat this as UB is quite huge. 'Course I may be totally off here. Just take this as a disclaimer :-)
Compilers *do* treat it as UB. UB doesn't mean "crash"; UB can still do
what you want.
The point of the UB designation is to allow implementations to be
reasonably fast. If you reinterpret cast a pointer to a different type, the
compiler doesn't have to check to see if that object really exists there;
it will simply trust your cast and pretend that there is an object there.
What I meant by "treating it as UB" was in the same vein as, e.g., signed
integer overflow. Compilers generate code today that doesn't work anymore
if it relies/relied on signed integer overflow, although older optimizer
didn't "break" anything.
In the same vein, I'm sure we can imagine several transformations that
break code that has no "placement new" (from my OP) that used (and uses) to
work.
Such as?

Assuming a lack of signed integer overflow means that the compiler doesn't
have to insert code to *check* for integer overflow. The UB designation
allows correct code (code without overflows) to execute at maximum
performance. Any degrading of incorrect code is merely a consequence of
making correct code as fast as possible.

Let's say that you have a function that returns a `T*`. The fastest code
generated which uses this return value is code which assumes that `T*`
points to a live, valid object of type `T`. To do anything else makes
correct code slower. Even if you inlined that function or could otherwise
be certain that the `T*` was not valid, that simply means UB happens. Do
you think compiler writers are going to detect such circumstances and make
the code fail in some way?

Can you give an example of these "several transformations"? How would they
speed up correct code?

It should also be noted that, well, we can trace this rule back at least 12
years. Compilers haven't done anything to break such code yet.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Martin Ba
2017-01-16 21:18:30 UTC
Permalink
Post by Nicol Bolas
Post by Martin Ba
I cannot tell you if any discussion has been had. However, ...
... So the only change has been essentially a defect fix that makes
unions actually work, in accord with the standard.
In at least 12 years of standardization, the committee has made no
substantive change to the causes of bringing an object into being. While
this is not conclusive, the fact that C++17 did put a fix into this section
means that they have looked at it and talked about it at some point. So I
would suggest that, if there was discussion about it, it did not progress
beyond discussion.
Thanks a lot for that wrap up!
-snip-
Post by Martin Ba
p.p.s.: My personal impression on the matter is that requiring the placement new for trivial types (like int, ...) is rather insane and the amount of real world code compiled with C++ compilers
that would be broken should any C++ compiler/optimizer ever manage to actually treat this as UB is quite huge. 'Course I may be totally off here. Just take this as a disclaimer :-)
Compilers *do* treat it as UB. UB doesn't mean "crash"; UB can still do
what you want.
The point of the UB designation is to allow implementations to be
reasonably fast. If you reinterpret cast a pointer to a different type, the
compiler doesn't have to check to see if that object really exists there;
it will simply trust your cast and pretend that there is an object there.
What I meant by "treating it as UB" was in the same vein as, e.g., signed
integer overflow. Compilers generate code today that doesn't work anymore
if it relies/relied on signed integer overflow, although older optimizer
didn't "break" anything.
In the same vein, I'm sure we can imagine several transformations that
break code that has no "placement new" (from my OP) that used (and uses) to
work.
Such as?
Assuming a lack of signed integer overflow means that the compiler doesn't
have to insert code to *check* for integer overflow. The UB designation
allows correct code (code without overflows) to execute at maximum
performance. Any degrading of incorrect code is merely a consequence of
making correct code as fast as possible.
See e.g.:
http://stackoverflow.com/questions/7682477/why-does-integer-overflow-on-x86-with-gcc-cause-an-infinite-loop
... "The compiler assumes you won't cause undefined behavior, and optimizes
away the loop test."


Let's say that you have a function that returns a `T*`. The fastest code
Post by Nicol Bolas
generated which uses this return value is code which assumes that `T*`
points to a live, valid object of type `T`. To do anything else makes
correct code slower. Even if you inlined that function or could otherwise
be certain that the `T*` was not valid, that simply means UB happens. Do
you think compiler writers are going to detect such circumstances and make
the code fail in some way?
Can you give an example of these "several transformations"? How would they
speed up correct code?
In the same vein as gcc's -fdelete-null-pointer-checks - (see e.g.
http://stackoverflow.com/questions/23153445/can-branches-with-undefined-behavior-be-assumed-unreachable-and-optimized-as-dea)
the compiler sees a branch that definitiely invokes UB and optimizes away
the branch and the branch check.

It should also be noted that, well, we can trace this rule back at least 12
Post by Nicol Bolas
years. Compilers haven't done anything to break such code yet.
Yet. And I assume (FWIW) as a matter of QoI they won''t. But then, stuff
like -fwrapv and -fno-delete-null-pointer-checks have happened in the sense
that compiler writers saw legal optimization opportunities that break some
code. So, just because I or you cannot see any reason today, that's not
much consolation to me :-)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jens Maurer
2017-01-16 21:21:58 UTC
Permalink
What I meant by "treating it as UB" was in the same vein as, e.g., signed integer overflow. Compilers generate code today that doesn't work anymore if it relies/relied on signed integer overflow, although older optimizer didn't "break" anything.
In the same vein, I'm sure we can imagine several transformations that break code that has no "placement new" (from my OP) that used (and uses) to work.
Such as?
Here's a gentle introduction to undefined behavior vs.
optimizations:

http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html

And don't forget to follow the link to http://blog.regehr.org/archives/213 .

Jens
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Chris Hallock
2017-01-16 21:24:35 UTC
Permalink
Post by Martin Ba
In the same vein, I'm sure we can imagine several transformations that
Post by Martin Ba
break code that has no "placement new" (from my OP) that used (and uses) to
work.
Such as?
An aggressively-optimizing compiler that assumes perfectly-well-formed C++
input could detect that this code is UB and therefore assume it never
executes (i.e. dead code that can be omitted from the binary).
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
T. C.
2017-01-16 23:14:35 UTC
Permalink
Post by Nicol Bolas
Post by Martin Ba
In the same vein, I'm sure we can imagine several transformations that
break code that has no "placement new" (from my OP) that used (and uses) to
work.
Such as?
See Richard Smith's comment in this Reddit thread:
https://www.reddit.com/r/cpp/comments/5fk3wn/undefined_behavior_with_reinterpret_cast/dal28n0/
for an example.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jens Maurer
2017-01-16 21:11:12 UTC
Permalink
Hi.
I'm currently trying to understand a few ... interesting ... observations I have been making wrt. the C++ Standard and using char arrays as raw storage.
Essentially, as far as I can tell (have been told), the current C++ Standard only allows using a char array as raw storage (see also std::aligned_storage) when objects are put into this via placement new, even for e.g. int or other trivial(*) types.
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract machine!
::new (buf) int; // is this strictly required? (aside: it's obviously a no-op)
*((int*)buf) = 42; // for this discussion, just assume the cast itself yields the correct pointer value
}
What I would be interested in is whether this has been discussed in the committee (CWG?) in the last very few years
and whether there is any agreement if omitting the placement new (for trivial type) should be allowed or if Standard C++ should absolutely require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.

If you believe that intent is misguided, feel free to propose a change.
I'm sure compiler writers will explain to you how that substantially
pessimizes their code generation.
p.s.: (*) is "trivial type" the correct term?
p.p.s.: My personal impression on the matter is that requiring the placement new for trivial types (like int, ...) is rather insane and the amount of real world code compiled with C++ compilers
that would be broken should any C++ compiler/optimizer ever manage to actually treat this as UB is quite huge. 'Course I may be totally off here. Just take this as a disclaimer :-)
Some compilers might make special allowances for their particular user
community, precisely out of concerns you cited. That doesn't make your
code any better.

Jens
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Martin Ba
2017-01-16 21:49:45 UTC
Permalink
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
http://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's obviously
a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast itself
yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in the
committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new (for
trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
Yes, I very much feel the intent is misguided. For two reasons:

- This intent declares UB totally reasonable legacy code. At least I
consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char buffer
to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.

All the change I can propose is that CWG considers some way to make this
work. (As it does in practice anyway *today*.) As I understand so far from
what I gleaned from P0137R1 is that the problem we have at the moment is
that the definition for objects (in the memory location sense) doesn't
allow this and that it's pretty complex and hard to come up with something
that does allow it without restricting other things.


I'm sure compiler writers will explain to you how that substantially
pessimizes their code generation.
For this specific case, I do hope not. I'm braced for anything.
Post by Martin Ba
p.s.: (*) is "trivial type" the correct term?
p.p.s.: My personal impression on the matter is that requiring the
placement new for trivial types (like int, ...) is rather insane and the
amount of real world code compiled with C++ compilers
Post by Martin Ba
that would be broken should any C++ compiler/optimizer ever manage to
actually treat this as UB is quite huge. 'Course I may be totally off here.
Just take this as a disclaimer :-)
Some compilers might make special allowances for their particular user
community, precisely out of concerns you cited. That doesn't make your
code any better.
As far as the C++ Standard goes, I'm not so much concerned with "better"
but with not allowing future compilers to break reasonable legacy code.

*When* using char arrays (or malloc'ed memory) as backing store for trivial
types, I fully assume most (non generic) existing code to *not* employ
placement new, simply because it's the straightforward thing to (not) do
and the placement new would be a no-op and all compilers up to today seem
to generate working code.

I think, here, the C++ Standard should take into account this "existing
practice". (Yeah, I know the same arguments were/are raised wrt. signed
integer overflow or the nullpointer-check-elimination, but I at least feel
those cases, while possible problematic in quite some cases, are
historically quite more clear cut. And at least both affect C and C++ code
the same.)

cheers.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Jens Maurer
2017-01-16 22:41:30 UTC
Permalink
Post by Jens Maurer
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly communicated
Well, the C++ committee doesn't have a PR department. What's unclear about:

1.8p1 [intro.object]

"The constructs in a C++ program create, destroy, refer to, access, and manipulate objects.
An object is created by a definition (3.1), by a new-expression (5.3.4), when implicitly
changing the active member of a union (9.3), or when a temporary object is created
(4.4, 12.2). ..."
Post by Jens Maurer
and, I feel, rationalized. (Maybe it already has? Thats what the OP was actually about.)
I've reviewed the notes on the CWG discussions for P0137Rx and I could not
find anything that would directly talk about your example.
Post by Jens Maurer
If you believe that intent is misguided, feel free to propose a change.
* This intent declares UB totally reasonable legacy code.
Even legacy code should have used "memcpy" here.
Post by Jens Maurer
At least I consider it reasonable too *not* have to place a no-op placement new in straightforward buffer backed code for trivial types.
* Since C doesn't have placement new, any C code that uses a char buffer to back any other typed data is automatically UB in C++. Another unnecessary incompatibility.
All the change I can propose is that CWG considers some way to make this work. (As it does in practice anyway *today*.) As I understand so far from what I gleaned from P0137R1 is that the problem we have at the moment is that the definition for objects (in the memory location sense) doesn't allow this and that it's pretty complex and hard to come up with something that does allow it without restricting other things.
Well, without a specific proposal on the table for rules that make this
work, but don't detrimentally affect other cases, I'm afraid nothing much
will happen.
Post by Jens Maurer
*When* using char arrays (or malloc'ed memory) as backing store for trivial types, I fully assume most (non generic) existing code to *not* employ placement new, simply because it's the straightforward thing to (not) do and the placement new would be a no-op and all compilers up to today seem to generate working code.
When compilers introduced type-based alias analysis, there was lots of broken code
that could be made to work with -fno-strict-aliasing. The code, eventually, got
fixed. I'm sure people using char arrays as backing store will fix their code
eventually, or learn to live with the shame of -fobjects-spring-to-life eternally.
Post by Jens Maurer
I think, here, the C++ Standard should take into account this "existing practice". (Yeah, I know the same arguments were/are raised wrt. signed integer overflow or the nullpointer-check-elimination, but I at least feel those cases, while possible problematic in quite some cases, are historically quite more clear cut. And at least both affect C and C++ code the same.)
Again, without a proposal, nothing is likely to happen.
(Off-topic: The rules for signed bit-shifts are subtly different between
C and C++, last I looked.)

Jens
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-01-17 00:23:08 UTC
Permalink
Post by Martin Ba
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
Post by Martin Ba
See: http://stackoverflow.com/questions/41624685/is-placement-
new-legally-required-for-putting-an-int-into-a-char-array or related
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's obviously
a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast
itself yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in the
committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new (for
trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
- This intent declares UB totally reasonable legacy code. At least I
consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char
buffer to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.
The above code has undefined behavior in C too. C's effective type rules
do not permit changing the effective type of a declared object to something
other than its declared type; it only permits that for objects allocated
with malloc or similar.

In the case where the storage /was/ allocated through malloc or similar,
C++ requires a placement new where C simply allows the effective type to
change through a store (and some parts of the C effective type model don't
work as a result...). It would seem reasonable to me for such allocation
functions to be specified to have implicitly created whatever set of
objects the following code relies on existing[1] -- the compiler typically
has to make that pessimistic assumption anyway, since it doesn't know what
objects the implementation of an opaque function might create, so it seems
like we'd lose little and gain more C compatibility by guaranteeing
something like that.

[1]: that is, we could require the compiler to assume that malloc runs a
sequence of placement news (for types with trivial default construction and
destruction) before it returns, where that set is chosen to be whatever set
gives the program defined behavior -- if such a set exists
Post by Martin Ba
All the change I can propose is that CWG considers some way to make this
work. (As it does in practice anyway *today*.) As I understand so far from
what I gleaned from P0137R1 is that the problem we have at the moment is
that the definition for objects (in the memory location sense) doesn't
allow this and that it's pretty complex and hard to come up with something
that does allow it without restricting other things.
I'm sure compiler writers will explain to you how that substantially
pessimizes their code generation.
For this specific case, I do hope not. I'm braced for anything.
Post by Martin Ba
p.s.: (*) is "trivial type" the correct term?
p.p.s.: My personal impression on the matter is that requiring the
placement new for trivial types (like int, ...) is rather insane and the
amount of real world code compiled with C++ compilers
Post by Martin Ba
that would be broken should any C++ compiler/optimizer ever manage to
actually treat this as UB is quite huge. 'Course I may be totally off here.
Just take this as a disclaimer :-)
Some compilers might make special allowances for their particular user
community, precisely out of concerns you cited. That doesn't make your
code any better.
As far as the C++ Standard goes, I'm not so much concerned with "better"
but with not allowing future compilers to break reasonable legacy code.
*When* using char arrays (or malloc'ed memory) as backing store for
trivial types, I fully assume most (non generic) existing code to *not*
employ placement new, simply because it's the straightforward thing to
(not) do and the placement new would be a no-op and all compilers up to
today seem to generate working code.
I think, here, the C++ Standard should take into account this "existing
practice". (Yeah, I know the same arguments were/are raised wrt. signed
integer overflow or the nullpointer-check-elimination, but I at least feel
those cases, while possible problematic in quite some cases, are
historically quite more clear cut. And at least both affect C and C++ code
the same.)
cheers.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-01-17 01:47:36 UTC
Permalink
Post by Richard Smith
Post by Martin Ba
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
http://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's
obviously a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast
itself yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in
the committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new (for
trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
- This intent declares UB totally reasonable legacy code. At least I
consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char
buffer to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.
The above code has undefined behavior in C too. C's effective type rules
do not permit changing the effective type of a declared object to something
other than its declared type; it only permits that for objects allocated
with malloc or similar.
In the case where the storage /was/ allocated through malloc or similar,
C++ requires a placement new where C simply allows the effective type to
change through a store (and some parts of the C effective type model don't
work as a result...). It would seem reasonable to me for such allocation
functions to be specified to have implicitly created whatever set of
objects the following code relies on existing[1] -- the compiler typically
has to make that pessimistic assumption anyway, since it doesn't know what
objects the implementation of an opaque function might create, so it seems
like we'd lose little and gain more C compatibility by guaranteeing
something like that.
[1]: that is, we could require the compiler to assume that malloc runs a
sequence of placement news (for types with trivial default construction and
destruction) before it returns, where that set is chosen to be whatever set
gives the program defined behavior -- if such a set exists
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model does
not permit storage to have an indeterminate object or many separate objects
(outside of nesting). If you allocate 4 bytes and new an `int` into it,
then it is an int. If you new a `float` into it, it stops being an `int`.

So, how would you suggest the object model change to accommodate such a
thing? What is the syntax that causes a piece of storage that contains all
objects to contain just one?

Personally? I say let it go. C++ programmers have managed to survive this
being UB since at least 2004. We're teaching C++ programmers nowadays to
avoid pointless casting; the average C++ programmer today is far more
likely to employ placement-new than to do casts and assume it was
constructed.

I'd rather the committee spend time shoring up the object model for genuine
C++ purposes, like making it possible for `vector` to be implemented
without UB.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-01-17 02:04:49 UTC
Permalink
Post by Nicol Bolas
Post by Richard Smith
Post by Martin Ba
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
Post by Martin Ba
See: http://stackoverflow.com/questions/41624685/is-placement-
new-legally-required-for-putting-an-int-into-a-char-array or related
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's
obviously a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast
itself yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in
the committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new (for
trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
- This intent declares UB totally reasonable legacy code. At least I
consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char
buffer to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.
The above code has undefined behavior in C too. C's effective type rules
do not permit changing the effective type of a declared object to something
other than its declared type; it only permits that for objects allocated
with malloc or similar.
In the case where the storage /was/ allocated through malloc or similar,
C++ requires a placement new where C simply allows the effective type to
change through a store (and some parts of the C effective type model don't
work as a result...). It would seem reasonable to me for such allocation
functions to be specified to have implicitly created whatever set of
objects the following code relies on existing[1] -- the compiler typically
has to make that pessimistic assumption anyway, since it doesn't know what
objects the implementation of an opaque function might create, so it seems
like we'd lose little and gain more C compatibility by guaranteeing
something like that.
[1]: that is, we could require the compiler to assume that malloc runs a
sequence of placement news (for types with trivial default construction and
destruction) before it returns, where that set is chosen to be whatever set
gives the program defined behavior -- if such a set exists
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model does
not permit storage to have an indeterminate object or many separate objects
(outside of nesting). If you allocate 4 bytes and new an `int` into it,
then it is an int. If you new a `float` into it, it stops being an `int`.
I never said they would all be at the start of the allocation.

So, how would you suggest the object model change to accommodate such a
Post by Nicol Bolas
thing? What is the syntax that causes a piece of storage that contains all
objects to contain just one?
Personally? I say let it go. C++ programmers have managed to survive this
being UB since at least 2004. We're teaching C++ programmers nowadays to
avoid pointless casting; the average C++ programmer today is far more
likely to employ placement-new than to do casts and assume it was
constructed.
I'd rather the committee spend time shoring up the object model for
genuine C++ purposes, like making it possible for `vector` to be
implemented without UB.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Nicol Bolas
2017-01-17 02:25:25 UTC
Permalink
Post by Richard Smith
Post by Nicol Bolas
Post by Martin Ba
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
http://stackoverflow.com/questions/41624685/is-placement-new-legally-required-for-putting-an-int-into-a-char-array
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's
obviously a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast
itself yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in
the committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new
(for trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
- This intent declares UB totally reasonable legacy code. At least
I consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char
buffer to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.
The above code has undefined behavior in C too. C's effective type
rules do not permit changing the effective type of a declared object to
something other than its declared type; it only permits that for objects
allocated with malloc or similar.
In the case where the storage /was/ allocated through malloc or similar,
C++ requires a placement new where C simply allows the effective type to
change through a store (and some parts of the C effective type model don't
work as a result...). It would seem reasonable to me for such allocation
functions to be specified to have implicitly created whatever set of
objects the following code relies on existing[1] -- the compiler typically
has to make that pessimistic assumption anyway, since it doesn't know what
objects the implementation of an opaque function might create, so it seems
like we'd lose little and gain more C compatibility by guaranteeing
something like that.
[1]: that is, we could require the compiler to assume that malloc runs
a sequence of placement news (for types with trivial default construction
and destruction) before it returns, where that set is chosen to be whatever
set gives the program defined behavior -- if such a set exists
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model does
not permit storage to have an indeterminate object or many separate objects
(outside of nesting). If you allocate 4 bytes and new an `int` into it,
then it is an int. If you new a `float` into it, it stops being an `int`.
I never said they would all be at the start of the allocation.
... that doesn't make sense. I mean, where else are they going to be
*except* for the start? If I allocate 4 bytes, then you need to `new` up
both `int` and `float` (assuming they're both 4 bytes, of course). But
there's no room to `new` them at different addresses within that
allocation, since the allocation is only 4 bytes.

So where would you be allocating these different objects?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-01-17 04:09:56 UTC
Permalink
Post by Richard Smith
Post by Nicol Bolas
Post by Martin Ba
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
Post by Martin Ba
See: http://stackoverflow.com/questions/41624685/is-placement-
new-legally-required-for-putting-an-int-into-a-char-array or related
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's
obviously a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast
itself yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in
the committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new
(for trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
- This intent declares UB totally reasonable legacy code. At least
I consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char
buffer to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.
The above code has undefined behavior in C too. C's effective type
rules do not permit changing the effective type of a declared object to
something other than its declared type; it only permits that for objects
allocated with malloc or similar.
In the case where the storage /was/ allocated through malloc or similar,
C++ requires a placement new where C simply allows the effective type to
change through a store (and some parts of the C effective type model don't
work as a result...). It would seem reasonable to me for such allocation
functions to be specified to have implicitly created whatever set of
objects the following code relies on existing[1] -- the compiler typically
has to make that pessimistic assumption anyway, since it doesn't know what
objects the implementation of an opaque function might create, so it seems
like we'd lose little and gain more C compatibility by guaranteeing
something like that.
[1]: that is, we could require the compiler to assume that malloc runs
a sequence of placement news (for types with trivial default construction
and destruction) before it returns, where that set is chosen to be whatever
set gives the program defined behavior -- if such a set exists
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model does
not permit storage to have an indeterminate object or many separate objects
(outside of nesting). If you allocate 4 bytes and new an `int` into it,
then it is an int. If you new a `float` into it, it stops being an `int`.
I never said they would all be at the start of the allocation.
... that doesn't make sense. I mean, where else are they going to be
*except* for the start? If I allocate 4 bytes, then you need to `new` up
both `int` and `float` (assuming they're both 4 bytes, of course). But
there's no room to `new` them at different addresses within that
allocation, since the allocation is only 4 bytes.


I don't know what this example is supposed to demonstrate.

So where would you be allocating these different objects?


If you allocate 8 bytes, there could be an int object at offset 0 and a
float object at offset 4.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Demi Obenour
2017-01-17 05:36:42 UTC
Permalink
I very much disagree.

C++ can use placement new, true. But C cannot, and many programs need to
compile and run as both.

Furthermore, I don't know of any reasonable way a compiler could exploit
this to produce better code. Strict aliasing doesn't apply, since char
pointers can alias anything. More importantly, you make it impossible to
take an aligned char array — say, one filled in by an I/O operation — and
cast it to an array of (say) int without an O(n) copy and a 2x memory
overhead! That is anything BUT fast. C++ should NOT impose such overheads.
Post by Nicol Bolas
Post by Richard Smith
Post by Martin Ba
Post by Martin Ba
Hi.
I'm currently trying to understand a few ... interesting ...
observations I have been making wrt. the C++ Standard and using char arrays
as raw storage.
Post by Martin Ba
Essentially, as far as I can tell (have been told), the current C++
Standard only allows using a char array as raw storage (see also
std::aligned_storage) when objects are put into this via placement new,
even for e.g. int or other trivial(*) types.
Post by Martin Ba
See: http://stackoverflow.com/questions/41624685/is-placement-
new-legally-required-for-putting-an-int-into-a-char-array or related
Post by Martin Ba
|alignas(int) char buf[sizeof(int)];
void f() {
// turn the memory into an int: (??) from the POV of the abstract
machine!
Post by Martin Ba
::new (buf) int; // is this strictly required? (aside: it's
obviously a no-op)
Post by Martin Ba
*((int*)buf) = 42; // for this discussion, just assume the cast
itself yields the correct pointer value
Post by Martin Ba
}
What I would be interested in is whether this has been discussed in
the committee (CWG?) in the last very few years
Post by Martin Ba
and whether there is any agreement if omitting the placement new (for
trivial type) should be allowed or if Standard C++ should absolutely
require the placement new./
I believe I can say that CWG agrees that the words now in C++17 correctly
reflect the intent that you need the placement new in the case above.
If this is really the intent, then this needs to be more clearly
communicated and, I feel, rationalized. (Maybe it already has? Thats what
the OP was actually about.)
If you believe that intent is misguided, feel free to propose a change.
- This intent declares UB totally reasonable legacy code. At least I
consider it reasonable too *not* have to place a no-op placement new in
straightforward buffer backed code for trivial types.
- Since C doesn't have placement new, any C code that uses a char
buffer to back any other typed data is automatically UB in C++. Another
unnecessary incompatibility.
The above code has undefined behavior in C too. C's effective type rules
do not permit changing the effective type of a declared object to something
other than its declared type; it only permits that for objects allocated
with malloc or similar.
In the case where the storage /was/ allocated through malloc or similar,
C++ requires a placement new where C simply allows the effective type to
change through a store (and some parts of the C effective type model don't
work as a result...). It would seem reasonable to me for such allocation
functions to be specified to have implicitly created whatever set of
objects the following code relies on existing[1] -- the compiler typically
has to make that pessimistic assumption anyway, since it doesn't know what
objects the implementation of an opaque function might create, so it seems
like we'd lose little and gain more C compatibility by guaranteeing
something like that.
[1]: that is, we could require the compiler to assume that malloc runs a
sequence of placement news (for types with trivial default construction and
destruction) before it returns, where that set is chosen to be whatever set
gives the program defined behavior -- if such a set exists
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model does
not permit storage to have an indeterminate object or many separate objects
(outside of nesting). If you allocate 4 bytes and new an `int` into it,
then it is an int. If you new a `float` into it, it stops being an `int`.
So, how would you suggest the object model change to accommodate such a
thing? What is the syntax that causes a piece of storage that contains all
objects to contain just one?
Personally? I say let it go. C++ programmers have managed to survive this
being UB since at least 2004. We're teaching C++ programmers nowadays to
avoid pointless casting; the average C++ programmer today is far more
likely to employ placement-new than to do casts and assume it was
constructed.
I'd rather the committee spend time shoring up the object model for
genuine C++ purposes, like making it possible for `vector` to be
implemented without UB.
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-01-17 07:41:22 UTC
Permalink
Post by Demi Obenour
C++ can use placement new, true. But C cannot, and many programs need to
compile and run as both.
That argument doesn't apply. You can write code that compiles as both C and
C++, but that does not mean the rules from one language apply in the other.

If you need to write C++-specific code, you can always just use #ifdef
__cplusplus.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-01-17 10:47:20 UTC
Permalink
Post by Nicol Bolas
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model
does not permit storage to have an indeterminate object or many separate
objects (outside of nesting).
Does it not? Could you provide a reference to the standard?

I have always assumed the following was a well defined code:

char* p = static_cast< char* >(malloc(sizeof(int) + sizeof(float)));
int* pi = new (p) int;
float* pf = new (p + sizeof(int)) float;

(null checks and alignment accounting skipped for brevity).

Regarding the OP, I think it is fair to say that malloc returns a
storage, which is a sequence of bytes (chars) that is allowed to alias
any other type. In that sense, the compiler has no way to know what
actual objects are created by malloc in that storage, so when the user
cases the returned pointer, type aliasing effectively happens. Whether
that is UB or not is a grey area because we don't know if the storage
actually contains the objects that we casted the pointer returned my
malloc to. Regardless, the compiler cannot assume that the code is UB
and e.g. remove it.
Post by Nicol Bolas
Personally? I say let it go. C++ programmers have managed to survive
this being UB since at least 2004. We're teaching C++ programmers
nowadays to avoid pointless casting; the average C++ programmer today is
far more likely to employ placement-new than to do casts and assume it
was constructed.
I disagree, because it requires programmers to write pointless code that
is known to be no-op anyway, just to satisfy the spec.

char* p = static_cast< char* >(malloc(sizeof(int) * 10));

// What is this code written for?
char* pi = p, *pe = p + sizeof(int) * 10;
for (; pi != pe; pi += sizeof(int))
{
new (pi) int;
}

// Use the array of ints
int* q = reinterpret_cast< int* >(p);
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-01-17 10:50:52 UTC
Permalink
Post by Andrey Semashev
Post by Nicol Bolas
The result of a "sequence of placement news" on a piece of memory is the
creation of an object of the last type `new`ed. The C++ object model
does not permit storage to have an indeterminate object or many separate
objects (outside of nesting).
Does it not? Could you provide a reference to the standard?
char* p = static_cast< char* >(malloc(sizeof(int) + sizeof(float)));
int* pi = new (p) int;
float* pf = new (p + sizeof(int)) float;
(null checks and alignment accounting skipped for brevity).
Regarding the OP, I think it is fair to say that malloc returns a
storage, which is a sequence of bytes (chars) that is allowed to alias
any other type. In that sense, the compiler has no way to know what
actual objects are created by malloc in that storage, so when the user
cases the returned pointer,
so when the user casts...
Post by Andrey Semashev
type aliasing effectively happens. Whether
that is UB or not is a grey area because we don't know if the storage
actually contains the objects that we casted the pointer returned my
malloc to. Regardless, the compiler cannot assume that the code is UB
and e.g. remove it.
Post by Nicol Bolas
Personally? I say let it go. C++ programmers have managed to survive
this being UB since at least 2004. We're teaching C++ programmers
nowadays to avoid pointless casting; the average C++ programmer today is
far more likely to employ placement-new than to do casts and assume it
was constructed.
I disagree, because it requires programmers to write pointless code that
is known to be no-op anyway, just to satisfy the spec.
char* p = static_cast< char* >(malloc(sizeof(int) * 10));
// What is this code written for?
char* pi = p, *pe = p + sizeof(int) * 10;
for (; pi != pe; pi += sizeof(int))
{
new (pi) int;
}
// Use the array of ints
int* q = reinterpret_cast< int* >(p);
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Loading...