Tuesday, February 25, 2020

Digest for comp.lang.c++@googlegroups.com - 24 updates in 6 topics

Bonita Montero <Bonita.Montero@gmail.com>: Feb 18 06:46PM +0100

> What good reasons are left for a literal goto? I can see it in C for
> error handling (because it lacks exceptions) - in C++ there is hardly
> a good reason IMHO.
 
Even MISRA:C 2012 allows gotos if they go forward.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Feb 25 07:56AM -0800


> Hmm, can't one simply compute:
 
> ~(size_t)0 ^ (~(size_t)0 >> 1)
 
> ?
 
Why not just this? (size_t)-1 /2 +1
cross@spitfire.i.gajendra.net (Dan Cross): Feb 25 10:00PM

In article <86y2sqace9.fsf@linuxsc.com>,
>Why not just this? (size_t)-1 /2 +1
 
Because I didn't think of it before you posted
it, Tim. :-)
 
- Dan C.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 25 12:34AM

On 24 Feb 2020 22:53:17 GMT
> > Ken Thompson.
 
> There's something wrong with that story: according to Wikipedia, Go
> was designed in 2007.
 
Here's a varbatim extract from what Rob Pike said about it in a talk in
2012 (text licensed by him under the Creative Commons Attribution 3.0
License). Please don't argue with me about whether he is right or not:
take it up with him if you feel strongly about it. (I happen to think
C++11 was pretty good.)
 
Back around September 2007, I was doing some minor but central work
on an enormous Google C++ program, one you've all interacted with,
and my compilations were taking about 45 minutes on our huge
distributed compile cluster. An announcement came around that there
was going to be a talk presented by a couple of Google employees
serving on the C++ standards committee. They were going to tell us
what was coming in C++0x, as it was called at the time. (It's now
known as C++11).
 
In the span of an hour at that talk we heard about something like 35
new features that were being planned. In fact there were many more,
but only 35 were described in the talk. Some of the features were
minor, of course, but the ones in the talk were at least significant
enough to call out. Some were very subtle and hard to understand, like
rvalue references, while others are especially C++-like, such as
variadic templates, and some others are just crazy, like user-defined
literals.
 
At this point I asked myself a question: Did the C++ committee really
believe that was wrong with C++ was that it didn't have enough
features?
 
***
 
But the C++0x talk got me thinking again. One thing that really
bothered me—and I think Ken and Robert as well—was the new C++ memory
model with atomic types. It just felt wrong to put such a
microscopically-defined set of details into an already over-burdened
type system. It also seemed short-sighted, since it's likely that
hardware will change significantly in the next decade and it would be
unwise to couple the language too tightly to today's hardware.
 
We returned to our offices after the talk. I started another
compilation, turned my chair around to face Robert, and started asking
pointed questions. Before the compilation was done, we'd roped Ken in
and had decided to do something. We did not want to be writing in C++
forever, and we—me especially—wanted to have concurrency at my
fingertips when writing Google code. We also wanted to address the
problem of "programming in the large" head on, about which more later.
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 25 02:00AM -0500

Chris Vine wrote:
> // and does not point to a Y object
> const int g = q->z; // OK
> const int h = std::launder(reinterpret_cast<Y*>(&s))->z; // OK
IMHO it is UB type punning either way (i.e. with or without std::launder) (I
know that the code is from cppreference). Please see the whole argument in
"Union type punning in C++ redux thread". Regardless of whether you agree to my
position in there, I think in case of std::launder, the Standard is quite clear
(for a change) so citing the complete subsection (by n4849):
 
"
17.6.4 Pointer optimization barrier [ptr.launder]
template<class T> [[nodiscard]] constexpr T* launder(T* p) noexcept;
 
1 Mandates: !is_function_v<T> && !is_void_v<T> is true.
 
2 Preconditions: p represents the address A of a byte in memory. An object X
that is within its lifetime (6.7.3) and whose type is similar (7.3.5) to T is
located at the address A. All bytes of storage that would be reachable through
the result are reachable through p (see below).
 
3 Returns: A value of type T* that points to X.
 
4 Remarks: An invocation of this function may be used in a core constant
expression whenever the value of its argument may be used in a core constant
expression. A byte of storage is reachable through a pointer value that points
to an object Y if it is within the storage occupied by Y, an object that is
pointer-interconvertible with Y, or the immediately-enclosing array object if Y
is an array element.
 
5 [Note: If a new object is created in storage occupied by an existing object of
the same type, a pointer to the original object can be used to refer to the new
object unless its complete object is a const object or it is a base class
subobject; in the latter cases, this function can be used to obtain a usable
pointer to the new object. See 6.7.3. — end note]
 
6 [Example:
struct X { int n; };
const X *p = new const X{3};
const int a = p->n;
new (const_cast<X*>(p)) const X{5}; // p does not point to new object (6.7.3)
// because its type is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK
— end example]
"
 
Please note:
 
a) the name of the section, std::launder is a [compiler -- IMHO] *optimization
barrier*.
 
b) that to call std::launder the type of the object currently in memory shall be
"similar" to the type argument of std::launder template and hence to the type to
which p points. Note that how exactly the p is received does not matter (so the
type of the object from which it was reinterpret_cast does not matter) -- it is
exactly the point of std::launder to "build" (see below) from the pointer to the
old object a pointer to the new object (of _similar type_ and through which all
bytes of the storage occupied by the new object shall be _reachable_) occupying
the same memory that the old object occupied before.
 
c) the [Note:]. In Library descriptions, Notes are informative rather than
normative so my guess it is here exactly and only to answer "why" question. The
Note claims that, in general the pointer to the old object can be simply used to
access the new object -- "unless its complete object is a const object or it is
a base class subobject" -- as I briefly cited above -- which case is exactly the
reason for launder to be called. [Note] hints at kinds of optimizations the
"Pointer optimization barrier" intends to prohibit: in particular I would
speculate that it needs to prohibit at least optimizing out reads via the old
pointer-to-const or pointer-to-base subobject that compiler otherwise could
replace with e.g. the values earlier loaded via the old pointer to registers
(the former -- by reasoning that the const object shall not have changed; and
the latter -- because the destruction and recreation of a derived object could
be not traceable or not required by the Standard to be traced by the compiler if
it can only see the code operating with a pointer to a base subobject of that
derived object).
 
d) finally, that the [Example] in the Standard is in direct and precise
correspondence with the [Note] -- as opposed to the example at cppreference.
Specifically, the [Example] does not use reinterpret_cast or static_cast
distractions but instead demonstrates how the pointer to the destroyed const
object can be used to build a pointer through which an object created later in
the same storage ("new object") could be safely accessed. (At runtime, such
"build" itself is probably no-op and the new pointer can very well have same
value and even occupy same memory location or a register as the old pointer --
but at compile time the compiler would know to build the code that actually
accesses the storage for new accesses via so built new pointer -- instead of
using the values read earlier via the old pointer and cached somewhere. In other
words, std::launder seems to be an explicit anti-aliasing of sort for similar
types).
 
 
Jorgen Grahn <grahn+nntp@snipabacken.se>: Feb 25 07:30AM

On Tue, 2020-02-25, Chris Vine wrote:
 
> Here's a varbatim extract from what Rob Pike said about it in a talk in
> 2012 (text licensed by him under the Creative Commons Attribution 3.0
> License).
 
[snip quote]
 
Thanks! It made more sense in his words.
 
> Please don't argue with me about whether he is right or not: take it
> up with him if you feel strongly about it. (I happen to think C++11
> was pretty good.)
 
So do I -- to me, it's mostly a polished C++98, supporting the same
things that C++98 did, only better. Although unlike Pike I don't care
about concurrency ...
 
I complained mostly because I didn't think work on (what became) C++11
had started in 2007, but thinking again of the C++0x name, of course
it had.
 
/Jorgen
 
--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 25 11:01AM

On 25 Feb 2020 07:30:10 GMT
> On Tue, 2020-02-25, Chris Vine wrote:
[snip]
 
> So do I -- to me, it's mostly a polished C++98, supporting the same
> things that C++98 did, only better. Although unlike Pike I don't care
> about concurrency ...
 
While I think that C++11 was pretty good - rvalue references and
variadic templates were like a breath of fresh air - and I can see the
point of concepts and modules in C++20, it seems to me that C++ is
becoming so complex that it is beyond the ability of the normal
programmer to come to grips with all of it. This results in bugs. C++
has grown as a loosely pinned set of features, not all of which are
consistent and which are a mess of corner cases where one edge meets
another. Some features look as if they were championed by dilletantes,
not by practising programmers. It lacks regularity and consistency.
C++ is not alone in complexity: Haskell for example is also complex and
difficult to grasp (although it is largely regular), but Haskell is not
a mainstream programming language like C++ is supposed to be.
 
This doesn't worry me especially. I use other languages also (not yet
rust), including functional languages, and enjoy them. However I do get
annoyed by gratuitous code-breaking things in C++, like needing to apply
std::launder to buffers updated with placement new. Surely we must all
have written circular buffers which construct elements in place with
placement new? What's the point with std::launder for heaven's sake -
the compiler can see you have used placement new, that necessarily
implies that the memory location concerned has been mutated, so when
code accesses the buffer with the correct cast to meet aliasing rules,
make sure the dereferenced values supplied are the correct ones. You
shouldn't need to burden the programmer with having to remember to use
std::launder to get the correct code generated.
 
I am not convinced even the experts, such as those at cppreference.com
or those on the standard committee, understand the rules on std::launder
and placement new. I notice that C++20 has now rowed back from C++17 on
some of this: in reponse to protestations, you no longer need to use
std::launder to access an object constructed by placement new by reason
of it having a const or reference member. However, nothing has changed
on using std::launder with aligned char[]/std::byte[] buffers. I am
still not 100% sure std::launder is required for this and that
cppreference.com is right on the point, but nor does anyone else seem to
be.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 25 12:13PM

On Tue, 25 Feb 2020 02:00:42 -0500
> know that the code is from cppreference). Please see the whole argument in
> "Union type punning in C++ redux thread". Regardless of whether you agree to my
> position in there,
 
I completely disagree with you that leaving aside std::launder it is UB,
so that does somewhat colour my view on the remainder of your views. It
seems to me that you are just plucking this out of the air. Leaving
aside std::launder, this is very well established code. If it didn't
work there would be no point in having placement new (you couldn't use
it in uninitialized memory returned by std::malloc() or operator new()
either). Something similar appears in Stroustrup's TC++PL.
 
> I think in case of std::launder, the Standard is quite clear
> (for a change) so citing the complete subsection (by n4849):
 
> 17.6.4 Pointer optimization barrier [ptr.launder]
[snip]
 
None of this deals with the issue now in question. §21.6.4 of C++17
is concerned with the preconditions to using std::launder (which would
be met here) and the effects thereof, but not with the circumstances in
which §6.8/8 of C++17 requires the use of std::launder. In the case
cited above, on the face of it there are a number of questions going to
the extent to which §6.8/8 of C++17 is relevant to begin with:
 
i. By virtue of the implicit decay of arrays to pointers, is the array
name, when combined with the pointer arithmetic used in accessing array
members, a "pointer that pointed to the original object".
 
ii. If so, does it cease to be so if (as above) the type of the buffer
element type (std::byte) is different from the type constructed in it
by placement new (type Y)?
 
iii. Does §6.8/8 have any relevance where, as in the case cited above,
placement new is used to construct an object in unninitialized memory
for the first time? (On the face of it §6.8/8 is concerned with
constructing a replacement object in initialized memory after the
lifetime of the previous object has ended).
Juha Nieminen <nospam@thanks.invalid>: Feb 25 01:51PM

> it seems to me that C++ is
> becoming so complex that it is beyond the ability of the normal
> programmer to come to grips with all of it.
 
This is something that has been being said, non-stop, for the past 20 years.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 25 01:55PM

On Tue, 25 Feb 2020 12:13:36 +0000
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
[snip]
> work there would be no point in having placement new (you couldn't use
> it in uninitialized memory returned by std::malloc() or operator new()
> either). Something similar appears in Stroustrup's TC++PL.
 
I see it is also specifically permitted by §4.5/3 of C++17:
 
If a complete object is created (8.3.4) in storage associated with
another object e of type "array of N unsigned char" or of type "array
of N std::byte" (21.2.1), that array provides storage for the created
object if:
 
* the lifetime of e has begun and not ended, and
* the storage for the new object fits entirely within e, and
* there is no smaller array object that satisfies these constraints.
 
The way you create an object in storage associated with another object
of array type is with placement new.
Melzzzzz <Melzzzzz@zzzzz.com>: Feb 25 01:57PM

>> becoming so complex that it is beyond the ability of the normal
>> programmer to come to grips with all of it.
 
> This is something that has been being said, non-stop, for the past 20 years.
Yeah, and response is very few use all of it :)
 
--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Svi smo svedoci - oko 3 godine intenzivne propagande je dovoljno da jedan narod poludi -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Feb 25 02:40PM

On Tue, 25 Feb 2020 13:51:55 -0000 (UTC)
> > becoming so complex that it is beyond the ability of the normal
> > programmer to come to grips with all of it.
 
> This is something that has been being said, non-stop, for the past 20 years.
 
And the relative use of C++ in projects has dropped in the past 20
years also. Whether that's because people are put off by C++'s
complexity and proneness to having undefined behavior spring out to get
you, or because they think other languages are better in other ways, is
difficult to say. Probably factors such as garbage collection,
developer support and type safety are also relevant.
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 25 12:26AM -0500

Bonita Montero wrote:
>> Care to pick one that is most readable in your opinion for me to demonstrate its
>> readability pitfalls?
 
> Pople able to manage to handle C++ can read either case good.
Popla manage not handle good.
Bonita Montero <Bonita.Montero@gmail.com>: Feb 19 07:28AM +0100

> I have never said it had bad readability. I said your "nice thing" serving
> no purpose was an obfuscation pretending to be simple while not being so. ...
Whoever finds the contradiction may keep it.
 
> return TSX_ABORTED_CAN_RETRY;
 
> return TSX_ABORTED_DONT_RETRY;
> }
 
That's a matter of taste.
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 17 04:04PM -0500

Bonita Montero wrote:
>>> some nice  things like this "++(xyz == CMP ? a : b)" with it.
 
>> Now THAT is a) obfuscation and b) irrelevant to your TM idea.
 
> It's unusual, but readable, so no obfuscation.
It's not really unusual. You "nice thing" pretends to be simple (which
it is not) only because it's example code serving no useful purpose.
Your original code served useful purpose and you immediately found you
had to correct it. The correction made your conditional expression
slightly bulkier and the use of ternary operator (that was originally
reasonable) lost 95% of its initial appeal. It's no longer concise and
not too readable even though it is free of side effects as opposed to
your "nice thing" above. Readable code separates concerns and your "nice
thing" does the opposite.
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 19 12:28AM -0500

Bonita Montero wrote:
>> your "nice thing" above. Readable code separates concerns and your "nice
>> thing" does the opposite.
 
> The code I've shown doesn't have a bad redability.
I have never said it had bad readability. I said your "nice thing" serving no
purpose was an obfuscation pretending to be simple while not being so. It would
be a bad idea (now I am saying "bad") to use it as an example to do anything useful.
 
Your original "transaction" code was rather readable although had an unnecessary
"else" clause made it more complex than necessary.
 
While fixing the bug your worsened the readability from ok to rather poor (again
I am not calling it "bad").
 
A better readable code for "transaction", after the bug fix and preserving your
preferences for indentation and braces but not new lines and using magic numbers
vs symbolic constants could be, for example:
 
enum TsxStatusCategory: int {
TSX_ABORTED_DONT_RETRY = -1,
TSX_ABORTED_CAN_RETRY = 0,
TSX_COMMITTED = 1
};
 
template<typename L>
inline
TsxStatusCategory
doTsxTransaction( L &lambda ) // function name should be a verb [clause]
{
unsigned code = _xbegin(); // unsinged is not unsigned BTW
 
if( code == _XBEGIN_STARTED )
{
lambda();
_xend();
return TSX_COMMITTED;
} // `else' served no purpose here other than obfuscation
 
if (code & _XABORT_EXPLICIT)
return TSX_ABORTED_DONT_RETRY;
 
if (code & _XABORT_RETRY)
return TSX_ABORTED_CAN_RETRY;
 
return TSX_ABORTED_DONT_RETRY;
}
 
 
> Yours has a bad readabiliy.
Yes, it does and as said earlier it is its point.
Bonita Montero <Bonita.Montero@gmail.com>: Feb 18 08:09AM +0100

> not too readable even though it is free of side effects as opposed to
> your "nice thing" above. Readable code separates concerns and your "nice
> thing" does the opposite.
 
The code I've shown doesn't have a bad redability.
Yours has a bad readabiliy.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Feb 17 04:22PM -0800

> Hello,
 
> Here is my new invention of a scalable algorithm:
[...]
 
Have you ever use a verification program? Like, oh, say Relacy?
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 25 12:03AM -0500

Öö Tiib wrote:
> to struct itself I already copy pasted. And aligned_storage
> is satisfying the alignment requirements of T so why you deny
> that Daniel's example is well-formed?
Because the above text requires that "there is an object b of type T (ignoring
cv-qualification) that is pointer-interconvertible (6.9.2) with a" for result to
be a pointer to b.
 
How can we deduce that the type std::aligned_storage<T,
some-align-greater-than-or-equal-to-alignof<T> >::type is
pointer-interconvertible with T (even ignoring cv-qualification)?
 
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 25 12:11AM -0500

Pavel wrote:
 
> How can we deduce that the type std::aligned_storage<T,
> some-align-greater-than-or-equal-to-alignof<T> >::type is
> pointer-interconvertible with T (even ignoring cv-qualification)?
More precisely I should have asked: \
 
How can we deduce that *an object of type* std::aligned_storage<T,
some-align-greater-than-or-equal-to-alignof<T> >::type is
pointer-interconvertible with *an object of type* T (even ignoring
cv-qualification)?
 
 
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 25 12:19AM -0500

Cholo Lennon wrote:
 
> Just for the record: there is a nice talk from CppCon 2019 about "Type punning
> in modern C++"
 
> https://youtu.be/_qzMpk-22cc
Thanks, it is a nice and somewhat funny talk. Among the others, there is a
chance that the OP's code will become legal in C++ 23 :-).
 
The guy is in "reinterpret_cast" camp but in the Q&A session he at least
acknowledges the existence of the "union" camp that believes that no use of
reinterpret_cast is legal for type punning. It seems someone from that other
camp wrote an article to disprove reinterpret_cast type punning .. funny I never
heard of it; will try to find and read it when idle i.e. probably also around
2023 when it really does not matter :-).
 
> Cholo Lennon
> Bs.As.
> ARG
 
-Pavel
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Feb 24 10:37PM -0800

On 2/24/2020 9:19 PM, Pavel wrote:
> camp wrote an article to disprove reinterpret_cast type punning .. funny I never
> heard of it; will try to find and read it when idle i.e. probably also around
> 2023 when it really does not matter :-).
 
Fwiw, what I get from the talk is that its better have a ctor and dtor,
or else its undefined regardless? Placement new and explicit dtor call
for properly aligned bytes.
Pavel <pauldontspamtolk@removeyourself.dontspam.yahoo>: Feb 19 01:21AM -0500

Daniel wrote:
> return a.tag;
> }
> };
I think no: it is not allowed to inspect a.tag regardless of the constructor
used to construct V because `tag' is not a part of the common initial sequence
of either V::a and V::b or of V::a and V::c (I think the common initial sequence
is empty in both cases).
> {
 
> }
> };
I think no, for same reason.
 
I think changing implementation of tag() to either of { return b.tag; } or {
return c.tag; } would make the code valid (then of course having V::a member
would be unnecessary).
 
> Thanks,
> Daniel
 
FWIW,
 
-Pavel
Real Troll <Real.Troll@Trolls.com>: Feb 17 10:10PM

> material: Howard Hinnant (lead designer and author of move semantics),
> Jens Maurer, Arthur O'Dwyer, Geoffrey Romer, Bjarne Stroustrup, Andrew
> Sutton, Ville Voutilainen, Jonathan Wakely.
 
<https://herbsutter.com/2020/02/17/move-simply/>
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: