Saturday, June 20, 2020

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

Sam <sam@email-scan.com>: Jun 15 07:07AM -0400

Chris M. Thomasson writes:
 
> header* const h = reinterpret_cast<header*>(std::malloc(sizeof(*h) +
> size));
 
> How infested with UB is this? Any bugs?
 
Eh, there's probably some teeny amount of UB here, but I would not worry
about it too much, the compilers' behavior should not be too surprising here.
 
And gcc even gives its formal blessing to the following:
 
https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
Manfred <noname@add.invalid>: Jun 17 05:41PM +0200

>>> right? In pure C...
 
>> In C yes, in C++ a type cast is required. A static_cast would suffice.
 
> A static_cast is just a more verbose C cast and the two are interchangable.
 
No, they are not.
 
https://en.cppreference.com/w/cpp/language/expressions#Conversions
Bo Persson <bo@bo-persson.se>: Jun 16 05:53PM +0200

On 2020-06-16 at 17:29, Paavo Helde wrote:
> trigger implicit object creation."
 
> I gather all this fuss is about allowing type-based alias analysis.
> Reinterpret_cast works directly against this idea.
 
Yes, the idea was to not have to decorate every other pointer with a
'restrict' keyword (like some other language does).
 
 
Bo Persson
boltar@nowhere.co.uk: Jun 17 03:20PM

On Wed, 17 Jun 2020 15:27:26 +0200
 
>> Why would one need to cast the return value? void* can do it as is,
>> right? In pure C...
 
>In C yes, in C++ a type cast is required. A static_cast would suffice.
 
A static_cast is just a more verbose C cast and the two are interchangable.
Juha Nieminen <nospam@thanks.invalid>: Jun 17 06:36AM

> Technically, constructing the 'header' object in the malloc'ed buffer
> is also reputed to be undefined behaviour if you do it otherwise that
> through placement new, even though 'header' is a trivial type.
 
Thinking about it, it might actually have merit to worry about such things
being UB, no matter how "technically" and how obscure the rule may be.
 
One could easily just think like "who cares if it's "technically" UB?
There's no practical implementation where it would cause anything else
than intended behavior."
 
The problem is, UB allows the compiler to do whatever it wants. Including
not doing what the programmer "intended" for it to do. Tehcnically speaking
if the compiler detects UB, it's allowed to think "this is UB, I don't need
to do anything here, I'll just skip the whole thing and optimize it all
away". Suddenly you might find yourself with an incredibly obscure
"compiler bug" where the compiler isn't generating the code you wrote...
when in fact it's not a compiler bug at all.
 
I remember a particularly nasty bug many years ago in the Linux kernel that
was caused precisely by this kind of thing. That part of the kernel code
was technically UB... and the compiler did whatever it wanted and not the
thing that the code was "intending" it to do (and, IIRC, it optimized that
part away, which caused the intended thing to not happen. I don't remember
now the exact reason, but might have had something to do with deliberately
dererencing a null pointer, which is UB, and which the compiler was
"optimizing" away because the standard allowed it to.)
Paavo Helde <eesnimi@osa.pri.ee>: Jun 16 12:15PM +0300

16.06.2020 02:26 Chris Vine kirjutas:
> new char[] expression is technically undefined behaviour. See
> https://stackoverflow.com/questions/60465235/does-stdunitialized-copy-have-undefined-behavior
> However practical implementations will allow it for reasonable uses.
 
Huh, good to know 99% of C programs formally contain UB when compiled as
C++.
 
> for this purpose). But even so dereferencing the result of 'this + 1'
> is I think technically also undefined behaviour if the buffer at that
> address has not been constructed there by placement new[],
 
Just to clarify: are you speaking about dereferencing 'this+1' as
another header object? Or about converting 'this+1' to a char* pointer
and dereferencing this? For the latter, at least the dereferencing
should be kosher if the initial buffer was allocated by new char[].
Manfred <noname@add.invalid>: Jun 17 07:13PM +0200


>> https://en.cppreference.com/w/cpp/language/expressions#Conversions
 
> Its close enough as substitute in 99% of circumstances. Though personally
> I prefer a C cast, C++ has enough verbiage as it is.
 
Read more carefully:
A C-style cast can perform a reinterpret_cast (possibly even combined
with a const_cast). This is a major difference with static_cast.
 
Moreover, verbosity has been widely publicized as intentional for cast
operators, for obvious reasons.
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Jun 16 05:44PM +0100

On Tue, 16 Jun 2020 17:53:56 +0200
Manfred <noname@add.invalid> wrote:
[snip]
> I wonder why the standard managed to come up with something that denies
> /any/ object allocation with malloc (at least according to the first
> example in the proposal).
 
It doesn't say you cannot construct objects in raw memory provided by
malloc (or by operator new for that matter). It says that to do it you
have to use placement new. (The problem with pointer arithmetic on
non-arrays is a different but also annoying fault.)
 
I think this insistence on using placement new is wrong. If an object
is of trivial type it does not have a constructor or destructor which
does anything. It is a C-like type. For trivial types, I see no
reason why the standard cannot take the dynamic type of the memory to
be the type of the first trivial object placed in it, say by memcpy or
by assignment, which is how the C standard determines the "effective
type" of memory allocated by malloc. A standard which regards
inter-operability with C as important ought to provide for this.
 
In C++ this has now been overlain since C++17 with std::launder. Prior
to C++17 it used to be assumed that the strict aliasing rules regarding
dereferencing pointers were what you needed to comply with. Put
shortly, it used to be that if there really was an object of type T
properly constructed at address n, then you could reinterpret_cast n
to pointer to T and dereference that pointer. That seems fair and
logical. However this no longer applies generally. Even if you are
fully compliant with the strict aliasing rules, unless you fall within
one of the cases described as "pointer-interconvertible" then a
reinterpret_cast of pointers is no longer enough. You also have to use
std::launder. Amongst other things, a pointer to the first element of
an array is not pointer-interconvertible to pointer to array even
though they are mandated by the standard to have the same address
(there is an implicit cast in the reverse direction). You have to use
std::launder as well as a reinterpret_cast. Likewise you have to use
std::launder to access an object allocated in a buffer by placement new
other than through the pointer returned by placement new. What's the
point of this? The strict aliasing rules should be enough.
 
And it seems from P0593 that the standard has problems on other matters
of memory allocation. How on earth, in a serious technical standard,
can you mandate implementers to provided functions such as
std::unitialized_copy which, if implemented according to the
specification, have undefined behaviour?
Bonita Montero <Bonita.Montero@gmail.com>: Jun 15 09:20PM +0200

> __sync_bool_compare_and_swap_16 and my linker can't find it:
> "undefined reference to `__sync_bool_compare_and_swap_16'"
> Any ideas ?
 
I got it. I have to compile it with -march=x86-64.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Jun 16 01:23PM -0700

On 6/16/2020 12:53 PM, Bonita Montero wrote:
> fences. So I have a private fence( mo_t o ) function that selects
> per ifdef if we have neither gcc, nor MSVC, i.e. unknown compilers
> are assumed to do no fencing on CAS until the code is adpated.
 
Well, perhaps create a model that can work as if the intrinsic RMW's are
naked, or relaxed if you will. In other words, treating
InterlockedExchangeAdd as if it has no implied memory order. Call it the
working base model for systems with relaxed memory ordering. Akin to
SPARC RMO.
 
It seems like you are always putting the membar after the RMW:
 
example:
_________________________
inline
std::pair<std::uintptr_t, std::uintptr_t>
std::atomic<std::pair<std::uintptr_t, std::uintptr_t>>::exchange( pair_t
desired, mo_t mo )
{
pair_t cmp = m_pair;
while( !cmpxchgPair( m_pair, cmp, desired ) );
atomic_thread_fence( mo );
return cmp;
}
_________________________
 
This is fine for an acquire barrier, but not for release. The release
membar should be _before_ the atomic RMW.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Jun 16 12:43PM -0700

On 6/15/2020 9:53 PM, Bonita Montero wrote:
>> If not, then you need to go rouge. Take careful note of the
>> memory_order_acq_rel membar.
 
> I inserted the appropriate fences.
 
Just make double sure to put them in the correct places within the code.
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 06:53AM +0200

> If not, then you need to go rouge. Take careful note of the
> memory_order_acq_rel membar.
 
I inserted the appropriate fences.
Bonita Montero <Bonita.Montero@gmail.com>: Jun 16 09:53PM +0200

>> I inserted the appropriate fences.
 
> Just make double sure to put them in the correct places within the code.
 
The are in the correct places. I've changed the code a bit because
the fences aren't necessary because the intrinsics have their own
fences. So I have a private fence( mo_t o ) function that selects
per ifdef if we have neither gcc, nor MSVC, i.e. unknown compilers
are assumed to do no fencing on CAS until the code is adpated.
"Chris M. Thomasson" <chris.m.thomasson.1@gmail.com>: Jun 15 02:16PM -0700

On 6/15/2020 1:59 PM, Chris M. Thomasson wrote:
 
> An atomic CAS with acquire semantics, the membar would go _after_ the CAS.
 
> An atomic CAS with release semantics, the membar would go _before_ the CAS.
 
Fwiw, think about the standalone (atomic_thread_fence) membars required
for a general purpose lock:
________________________________
Atomic RMW to take the lock
 
Acquire Membar
 
[critical section]
 
Release Membar
 
Atomic RMW to release the lock
________________________________
 
 
An acquire/release would look like:
________________________________
Release Membar
 
Atomic RMW
 
Acquire Membar
________________________________
 
 
See how the membars line up with the mutex case?
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Jun 16 11:34PM +0100

> An excellent teaching and calling:
 
> https://www.youtube.com/watch?v=L1-hxP7l7Rw
 
An excellent response to that teaching and calling:
 
https://www.youtube.com/watch?v=42UCpOzTbNU
 
/Flibble
 
--
"Snakes didn't evolve, instead talking snakes with legs changed into snakes." - Rick C. Hodgin
 
"You won't burn in hell. But be nice anyway." – Ricky Gervais
 
"I see Atheists are fighting and killing each other again, over who doesn't believe in any God the most. Oh, no..wait.. that never happens." – Ricky Gervais
 
"Suppose it's all true, and you walk up to the pearly gates, and are confronted by God," Byrne asked on his show The Meaning of Life. "What will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a world that is so full of injustice and pain. That's what I would say."
Christian Gollwitzer <auriocus@gmx.de>: Jun 16 08:40PM +0200

Am 16.06.20 um 19:05 schrieb Makis:
 
>> if ( !cond4 ) return;
 
>> Do Something();
 
> Very hard if you have to do some cleanup upon leaving
 
On the contrary, very easy. Use RAII and the compiler does the right
cleanup in the right order. In real cases, I allocate resources in
between the different conditions. That means a lot of conditions for the
cleanup sequence, too, (necessary in C without ++) unless you use RAII
consequently, which leaves this to the compiler.
 
Christian
Marcel Mueller <news.5.maazl@spamgourmet.org>: Jun 16 08:08PM +0200

> Does anyone else use fake switches and fake loops like this just to exploit the 'break' keyword?
 
I prefer
 
do
{
...
 
break;
} while(true);
 
It basically defines two labels.
One to repeat the current block: continue,
and one after the current block: break.
 
 
It can also be useful to write
 
do
{
...
 
} while (fasle);
 
This is especially useful to escape from a nested switch statement with
continue.
 
 
But all of them are not always better than goto. It mainly helps to keep
code guidelines that prohibit the use of goto.
 
E.g. in case of CAS loops I prefer goto retry; since it is no loop in
the logical application flow.
 
 
Marcel
Sam <sam@email-scan.com>: Jun 15 07:00AM -0400

> itself after deletions. Does it happen after every erase or only after a
> certain amount of imbalance occurs? When it does happen how long does it
> take and is there a way to force it to happen?
 
This is not specified in the standard, and there are no specified ways to
force a rebalance.
boltar@nowhere.co.uk: Jun 16 09:38AM

On Tue, 16 Jun 2020 21:21:24 +1200
 
>> If you think a major rebalance would only involved updating 3 nodes then
>> you have no idea how balancing works.
 
>I thought not.
 
You know there's this very useful tool called Google, you might want to
find out about it. In the meantime here's a useful page with a nice example
of a small 11 node tree before and after balancing. I suspect even a
beginner like you will be able to see that more than 3 node updates had to
happen to achieve that:
 
https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree
 
HTH
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Jun 16 08:47PM +0100


> No, but he did invent the lie you use today, again in ignorance.
 
> You're holding on to lies, Leigh. They keep you bound, and will cont-
> inue to do so until you begin to seek the truth.
 
And Satan invented fossils, yes?
 
/Flibble
 
--
"Snakes didn't evolve, instead talking snakes with legs changed into snakes." - Rick C. Hodgin
 
"You won't burn in hell. But be nice anyway." – Ricky Gervais
 
"I see Atheists are fighting and killing each other again, over who doesn't believe in any God the most. Oh, no..wait.. that never happens." – Ricky Gervais
 
"Suppose it's all true, and you walk up to the pearly gates, and are confronted by God," Byrne asked on his show The Meaning of Life. "What will Stephen Fry say to him, her, or it?"
"I'd say, bone cancer in children? What's that about?" Fry replied.
"How dare you? How dare you create a world to which there is such misery that is not our fault. It's not right, it's utterly, utterly evil."
"Why should I respect a capricious, mean-minded, stupid God who creates a world that is so full of injustice and pain. That's what I would say."
Scott Newman <scott69@gmail.com>: Jun 13 06:32PM +0200

> int length;
> var_char data[0..length];
> };
 
Do you like this ?
 
#include <cstddef>
#include <cassert>
 
template<typename T, std::size_t N>
struct Container;
 
template<typename T>
struct ContainerBase
{
T &operator []( std::size_t i );
private:
template<typename T, std::size_t N>
friend struct Container;
#if !defined(NDEBUG)
size_t n;

No comments: