Tuesday, August 9, 2016

Digest for comp.lang.c++@googlegroups.com - 16 updates in 4 topics

Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 09 08:40PM +0100

On Tue, 9 Aug 2016 11:19:04 -0700 (PDT)
> void * _Ptr = _Access(_Read_access, _Index);
> return *reinterpret_cast<value_type*>(_Ptr);
> }
 
What "do this" are you addressing? reinterpret_casts are essential for
some uses, and the standard describes what aliasing is allowed when
using them. The issue you first raised however was that of casting a
non-const pair with non-const members to a non-const pair with a const
first member, which is a very interesting question. Your extract
from Microsoft's code does not help a great deal with that: there are
numerous reasons why a reinterpret_cast may have been preferred by the
library writer to a static_cast here, and we do not know if it covers
your use case. And in writing their standard library, Microsoft can do
anything that their compiler will accept, irrespective of whether the
code in question complies with the standard or not.
 
Alf's reasoning was imperfect for the reasons I have mentioned but I
think his conclusion would still be right if std::pair is an aggregate,
for the reasons he has given. However, std::pair has a user defined
constructor so it is not an aggregate. Will you get away with it?
Probably; my intuition is that you will since it seems so innocuous.
It seems pathetic to reject the code simply because std::pair is not
an aggregate, and it is difficult to believe a compiler would do so. Is
it standard conforming? Dunno, but I suspect not.
 
Chris
Daniel <danielaparker@gmail.com>: Aug 09 03:16PM -0700

On Tuesday, August 9, 2016 at 3:41:11 PM UTC-4, Chris Vine wrote:
> It seems pathetic to reject the code simply because std::pair is not
> an aggregate, and it is difficult to believe a compiler would do so. Is
> it standard conforming? Dunno, but I suspect not.
 
Thanks, appreciate your thoughtful remarks.
 
It seems to me that the more pessimistic conclusion is most likely to be
correct.
 
I do wish the standard associative containers hadn't dictated that value_type
must be std::pair<const key, mapped_type>, it was unnecessary to do that,
they could have allowed any type that supported key and value accessors and
value modifier. As a result it looks like a broad class of associative
container implementations cannot be made compatible with standard library
associative container iterators, such as the Google C++ btree (which
attempts to do so with the cast), stx-btree (which, requiring copying, can't
support modifying values through iterators), and (my immediate interest)
sorted arrays of pairs.
 
Best regards,
Daniel
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 09 11:26PM +0100

On 09/08/2016 23:16, Daniel wrote:
> correct.
 
> I do wish the standard associative containers hadn't dictated that value_type
> must be std::pair<const key, mapped_type>, it was unnecessary to do that,
 
It was necessary to do that to ensure that a key cannot be changed once
added to the binary search tree.
 
/Flibble
Daniel <danielaparker@gmail.com>: Aug 09 04:02PM -0700

On Tuesday, August 9, 2016 at 6:26:48 PM UTC-4, Mr Flibble wrote:
> > must be std::pair<const key, mapped_type>, it was unnecessary to do that,
 
> It was necessary to do that to ensure that a key cannot be changed once
> added to the binary search tree.
 
No. That's an interface issue, not an implementation issue.
 
There is no problem if value_type is defined as a type having a key() accessor
that is non-modifying.
 
That's assuming it's the user of the class that you're worried about, who
might change the key. If it's a perverse class implementer you're worried
about, well, he could always remove a tree node and replace it with a new one
with a different key, const key or not.
 
Anyway, this is, while regrettable, unable to be changed.
 
Best regards,
Daniel
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 10 12:23AM +0100

On 10/08/2016 00:02, Daniel wrote:
 
>> It was necessary to do that to ensure that a key cannot be changed once
>> added to the binary search tree.
 
> No. That's an interface issue, not an implementation issue.
 
It is both.
 
 
> There is no problem if value_type is defined as a type having a key() accessor
> that is non-modifying.
 
You cannot allow the key to be changed by allowing access to it hence it
must be const.
 
> might change the key. If it's a perverse class implementer you're worried
> about, well, he could always remove a tree node and replace it with a new one
> with a different key, const key or not.
 
You don't get it: if a key in a binary search tree is changed the tree
can become corrupt and invalid.
 
 
> Anyway, this is, while regrettable, unable to be changed.
 
It isn't regrettable as it is the correct design.
 
/Flibble
Melzzzzz <mel@zzzzz.com>: Aug 09 11:29PM +0200

Given following program,
 
#include <cstdio>
#include <utility>
 
class A{
public:
A() {
printf("constructor\n");
}
A(A && moved) {
printf("move constructor\n");
}
~A(){
printf("destructor\n");
}
};
 
int main() {
A a;
A b = std::move(a);
}
 
I would expect that destructor of `a` wouldn't be called but:
[bmaxa@maxa-pc examples]$ g++ moved.cpp
[bmaxa@maxa-pc examples]$ ./a.out
constructor
move constructor
destructor
destructor
[bmaxa@maxa-pc examples]$
 
Is this defect, and what is point of moving around, if not?
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 09 11:05PM +0100

On Tue, 9 Aug 2016 23:29:26 +0200
> destructor
> [bmaxa@maxa-pc examples]$
 
> Is this defect, and what is point of moving around, if not?
 
It is not a defect. The destructor for any object must always run when
the object is destroyed, say by going out of scope.
 
Typically the destructor for a moved object will do nothing because
its resources have been moved out, but it may still have residuary
resources to relinquish. The move constructor and assignment operator
for a class must ensure that any object of the class is still in a
state after the move which is capable of such destruction. It is also
wise to design the class so that it can still be assigned to after the
move.
 
Chris
Paavo Helde <myfirstname@osa.pri.ee>: Aug 10 01:34AM +0300

On 10.08.2016 0:29, Melzzzzz wrote:
> destructor
> [bmaxa@maxa-pc examples]$
 
> Is this defect,
 
It's not defect, it's a feature!
 
In more detail, the C++ ideology prescribes that the object should be in
a valid state during the whole of its lifetime. In case of auto/stack
objects this means from the point of their definition until the end of
their scope. This is in contrast to e.g. C where often a bunch of
uninitialized (invalid state) variables is created right in the
beginning of the function, and the pointer values continue to hold now
invalid values after they have been free()-d, for example.
 
In par with this C++ ideology, the moved-from objects must still remain
in a valid state, as the name of the object is still in scope. This
means they may still be accessed, for example a new value may be
assigned to the object. If the object were left in invalid state, then
in general it could not be assigned a new value (unless it is a POD).
 
As the object is in a valid state, the destructor still needs to be
called when the object lifetime ends, to clean up that valid state. If
the object has been moved-from, this destructor typically does nothing
much, like deletes a nullptr (which is a non-op), but depending on the
exact state of the object it might do more.
 
The above is the philosophical point of view. This is reinforced by the
technical viewpoint: how should the scope cleanup code know if the
variable has been moved-from or not? It might have been moved in one
code branch and not in another. Even if moved-from, it might have been
assigned a new value after that which needs destruction. The simplest
solution is just to call the destructor always when an object goes out
of scope, and to rely on this destructor to do the right thing according
to the current valid state of the object.
 
> and what is point of moving around, if not?
 
The point is that the resources managed by the object have been moved
away, so the destructor has much less to do.
 
HTH
Paavo
Melzzzzz <mel@zzzzz.com>: Aug 10 12:56AM +0200

On Wed, 10 Aug 2016 01:34:15 +0300
 
> The above is the philosophical point of view. This is reinforced by
> the technical viewpoint: how should the scope cleanup code know if
> the variable has been moved-from or not?
 
It's solved in Rust pretty clean.
 
It might have been moved in
> simplest solution is just to call the destructor always when an
> object goes out of scope, and to rely on this destructor to do the
> right thing according to the current valid state of the object.
 
That also means that there is unnecessary code for maintaining state
and also run time errors in case that object is moved twice...
(what one should do if constructing from already moved object?)
In Rust moving is done in the compiler and compiler gives errors if
moved object is used after move (except assignment). Also compiler
doesn't call destructors on moved objects so there is no need to worry
about that.
 
 
 
> > and what is point of moving around, if not?
 
> The point is that the resources managed by the object have been moved
> away, so the destructor has much less to do.
 
Well, yes, but it would be nicer if there would not be need for
housekeeping ;)
 
 
> HTH
> Paavo
 
Thank you!
Melzzzzz <mel@zzzzz.com>: Aug 10 12:57AM +0200

On Tue, 9 Aug 2016 23:05:18 +0100
Chris Vine <chris@cvine--nospam--.freeserve.co.uk> wrote:
 
It is also
> wise to design the class so that it can still be assigned to after the
> move.
 
It looks to me like half baked solution.
 
> Chris
 
Thanks!
Chris Vine <chris@cvine--nospam--.freeserve.co.uk>: Aug 10 12:20AM +0100

On Wed, 10 Aug 2016 00:57:45 +0200
> > wise to design the class so that it can still be assigned to after
> > the move.
 
> It looks to me like half baked solution.
 
I don't think I would say it is half-baked. At any rate, it is
consistent with the C++ design philosophy.
 
It is also convenient. Not all resources maintained by a class should
necessarily be moved out. Furthermore, since in C++ any object can
be moved from by casting it to rvalue (ie not only temporaries), if a
moved-from object is subsequently assigned to, all its newly acquired
resources will need to be cleaned up again. No doubt you could have a
language which requires a compiler which deals with (and optimises for)
this automatically, but that is not what C++ does.
 
It is not really a problem to write classes this way. I am relaxed
about it. You very soon get used to it.
Marcel Mueller <news.5.maazl@spamgourmet.org>: Aug 09 09:56PM +0200

On 09.08.16 17.44, bitrex wrote:
> memory footprint, and it seems goofy to store pointers in memory when
> the size of the data structure you're actually interested in is smaller
> than the width of a pointer type...
 
std::map uses red black trees. They are intended to keep iterators valid
on manipulations. Indeed they consume much memory.
=> Use a sorted vector with binary search.
 
But, the data type bool[] is bad too as it uses at least one byte per
entry.(Simply because common CPUs cannot handle sub byte addressing in a
way that complies with the rules for C++ pointers.)
=> use std::bitset instead. (It depends on the platform if bitset<7> can
be packed into a single byte. Some platforms require C++ objects to be
aligned at a machine size word.)
 
Furthermore ASCII has only 7 bits. I.e. you need at most 127 entries to
cover the entire ASCII range. So if you use a
std::array<std::bitset<7>,128> it will take 128 bytes. Really no big deal.
 
In contrast a sorted vector of pair<char,std::bitset<7>> will take twice
as much space when all entries are defined. So it is only efficient when
you have less than 64 symbols (likely with 7 segments). However the code
to do the binary search will probably take more than you saved.
 
The same applies to your initialization code since you did not use
constexpr. It will take much more memory than the resulting data
structures, including std::map.
Unfortunately std::bitset is out at this point since it does not support
reasonable constexpr list initializers. So you have to revert to
unsigned char, at least for the initialization.
 
Last but not least trivial solutions are sometimes quite effective:
switch (character)
{case 0:
return std::bitset<7>(0b1111110);
case 1:
return std::bitset<7>(0b0110000);
//...
}
Most compilers will create reasonably optimized code for this.
 
If you want to have even more compact solutions put only continuous runs
of values into arrays:
 
static const bitset<7> Numbers[10] =
{ 0b1111110,
0b0110000,
//...
};
static const bitset<7> Alpha[6] = // or [26]
{ 0b1110111,
//...
};
 
character -= '0';
if (character <= '9' - '0')
return Numbers[character];
character -= 'A' - '0';
if (character <= 'F' - 'A')
return Alpha[character];
return bitset<7>(0);
 
 
Marcel
Vir Campestris <vir.campestris@invalid.invalid>: Aug 09 09:09PM +0100

On 09/08/2016 19:58, bitrex wrote:
> One likes to feel like one is clever, but it does seem like that's the
> most straightforward solution... ; )
 
https://en.wikipedia.org/wiki/KISS_principle
 
Andy
Mr Flibble <flibbleREMOVETHISBIT@i42.co.uk>: Aug 09 09:11PM +0100

On 09/08/2016 21:09, Vir Campestris wrote:
>> One likes to feel like one is clever, but it does seem like that's the
>> most straightforward solution... ; )
 
> https://en.wikipedia.org/wiki/KISS_principle
 
KISS is overused, obvious and not always possible: some complicated
problems require complicated solutions.
 
/Flibble
Christian Gollwitzer <auriocus@gmx.de>: Aug 09 10:55PM +0200

Am 09.08.16 um 20:36 schrieb Scott Lurndal:
> } else {
> return NO_BITMAP_FOR_CHARACTER;
> }
 
I second this. The map is definitely not sparse enough to justify a more
complex algorithm.
 
Christian
Vir Campestris <vir.campestris@invalid.invalid>: Aug 09 09:16PM +0100

On 09/08/2016 05:06, Alf P. Steinbach wrote:
>>> make them work.
 
>> Do you even READ the posts you are replying to?
 
> It's an ad bot.
 
It is? But it's far too stupid for that...
 
Back to the OP - remember that when you and I see 123.456 it's a hundred
and something, to some nationalities that full stop is a thousands
separator and the value is over a hundred thousand... number formatting
brings nightmares. Nearly as bad as dates.
 
Andy
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: