Thursday, December 17, 2015

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

Patricia Anaka <panakabear@gmail.com>: Dec 17 11:07AM -0800

Hello,
I am seeing a weird bug occur in my C++ program -- the bug only happens in my release build and not in the debug. I've tracked it down to a simple function -- a test to determine whether a point is inside a polygon.
 
I've used this function for years without a problem.
 
 
int point_in_polygon(float testx, float testy, Fpoint *points, int nvert)
{
int i, j, c = 0;
// printf("test xy = %f %f\n", testx, testy);
 
for (i = 0, j = nvert - 1; i < nvert; j = i++) {
// printf("i %d j %d\n", i, j);
// printf("points[i] = %f %f\n", points[i].x, points[i].y);
// printf("points[j] = %f %f\n", points[j].y, points[j].y);
 
if (((points[i].y>testy) != (points[j].y>testy)) &&
(testx < (points[j].x - points[i].x) * (testy - points[i].y) / (points[j].y - points[i].y) + points[i].x))
c = !c;
}
return c;
}
 
Here's some sample data I pass the function. It fails the test in the release build -- returns without ever triggering the c = !c line.
 
fp 337.333313 124.000000
point 0: 221.000000 50.000000
point 1: 421.000000 50.000000
point 2: 421.000000 190.000000
point 3: 221.000000 190.000000
point 4: 221.000000 50.000000
 
The weird thing is that if I uncomment those print statements, then the function starts working! So it seems like some kind of compiler issue, I don't know. Anyone know what might be the cause of a problem like this?
 
Thanks!
Victor Bazarov <v.bazarov@comcast.invalid>: Dec 17 02:27PM -0500

On 12/17/2015 2:07 PM, Patricia Anaka wrote:
> point 3: 221.000000 190.000000
> point 4: 221.000000 50.000000
 
> The weird thing is that if I uncomment those print statements, then the function starts working! So it seems like some kind of compiler issue, I don't know. Anyone know what might be the cause of a problem like this?
 
Bugs in compilers are not frequent, but like any other software,
compilers are written by people and people do make mistakes.
 
If adding output seems to fix the problem, it's likely the issue in the
*optimizer*, which creates bad machine code in some cases. I can only
recommend you to _turn optimization off_ either for the entire module or
for this function only (if possible via a pragma directive, see your
compiler documentation for that). You can extract this function in a
separate module of course, and disable optimizations on that module only.
 
Experiment with different optimization levels, as well.
 
Best of luck!
 
V
--
I do not respond to top-posted replies, please don't ask
Barry Schwarz <schwarzb@dqel.com>: Dec 17 12:00PM -0800

One of the differences between debug and release is the action of the
optimizer.
 
Since uncommenting the print statements seems to eliminate the
problem, this appears to be a case of the optimizer getting something
wrong. Some possible workarounds that come to mind are:
 
Place the function in a source file by itself and reduce the
optimization level (possibly even down to no optimization).
 
Restructure the right hand operand of <. You could move the
addition of points[i].x to the front of the expression. You could
also swap the order of the two multiplicands.
 
At the top of the for loop, extract the four x and y values from
points and place them in local variables. Change the if statement to
use these values instead.
 
On Thu, 17 Dec 2015 11:07:43 -0800 (PST), Patricia Anaka
 
--
Remove del for email
Paavo Helde <myfirstname@osa.pri.ee>: Dec 17 03:05PM -0600

Patricia Anaka <panakabear@gmail.com> wrote in
> happens in my release build and not in the debug.
 
> Here's some sample data I pass the function. It fails the test in the
> release build -- returns without ever triggering the c = !c line.
 
How do you know that c=!c line is never triggered? Relying on breakpoints
is not very trustworthy in optimized builds. Maybe the line is triggered
twice?
 
> if (((points[i].y>testy) != (points[j].y>testy)) &&
 
I once had a compiler which had troubles with comparing boolean values for
equality or inequality, but this was over 20 years ago and in a different
language IIRC.
 
Cheers
Paavo
Patricia Anaka <panakabear@gmail.com>: Dec 17 01:53PM -0800

On Thursday, December 17, 2015 at 2:00:28 PM UTC-6, Barry Schwarz wrote:
 
> At the top of the for loop, extract the four x and y values from
> points and place them in local variables. Change the if statement to
> use these values instead.
 
Thanks for the replies! I spent some time experimenting with restructuring the loop. It turns out that it was the little "j = i++" thing in the loop that caused it.
 
So in other words, this works.
 
int point_in_polygon(float testx, float testy, Fpoint *points, int nvert)
{
int i, j, c = 0;
 
for (i = 0, j = nvert - 1; i < nvert; i++) {
if (((points[i].y > testy) != (points[j].y > testy)) &&
(testx < (points[j].x - points[i].x) * (testy - points[j].y) / (points[j].y - points[i].y) + points[i].x))
c = !c;
 
j = i;
}
 
return c;
}
 
 
So weird!
Ian Collins <ian-news@hotmail.com>: Dec 17 12:47PM +1300

> David Brown wrote:
 
[attribution replaced, please don't snip them!]
 
> codewarrior, and sn on a regular basis and I can't remember any
> analysis picking up on this type of mistake with uninitialized
> members.
 
That would be a difficult case to spot (except maybe in a constructor)
because the compiler can't really tell if the member variable has been
initialised before it used in another member function.
 
--
Ian Collins
David Brown <david.brown@hesbynett.no>: Dec 17 12:59AM +0100

> allows me to do "int x_ = 0;" in the class declaration, but I can still
> forget, and it's still a different requirement on me than if x_ had been
> an std::vector<int>, or had static storage.
 
Ask yourself /why/ you are adding x_ to the class. What is it doing?
How does it affect the behaviour of the class? And in particular, how
does it affect the class's invariant? If it has no effect on the
invariant, it doesn't matter how it is initialised. But if it /does/
have a part to play, as one would normally expect, then you need to
initialise it correctly. Of course that means editing the constructor -
adding the member to the class definition is only half the job.
 
If the compiler always initialised it to 0, then you would /still/ have
to look through the constructor, and think through the process - is 0 an
appropriate initial value here? Having the compiler initialise it to 0
saves you a negligible amount of time - it's a few seconds work to add
the x_(0) to the constructor. Most of the time and effort is thinking
about it - and you must do that anyway. Having the compiler make a
default that is sometimes appropriate risks letting you get away with
not thinking - and that's a bad thing.
 
> variable. Is that even possible? I use msvc, clang, ghs, armcc,
> codewarrior, and sn on a regular basis and I can't remember any analysis
> picking up on this type of mistake with uninitialized members.
 
If you see these warnings a lot, your coding practice is bad. These
warnings show that you are doing something wrong. There are good for
catching the occasional typo or glip, but if you get them often you
should think about how you are writing your code.
 
The compiler won't be able to guess about initialisation across compile
units (it might be able to do it with link-time optimisation). It can
only hope to see the issue if you made your constructors inline along
with the class definition.
 
And note that there is nothing inherently wrong with leaving a variable
or member uninitialised - it is only attempting to read it before
initialisation that is wrong.
 
 
>> You don't mean "undefined", you mean "unspecified" or "indeterminate".
 
> Sorry - you're right again. I sometimes conflate undefined and
> unspecified. This is even worse than global/static. Very bad of me.
 
It's an easy mistake to make - but I think the distinction is important
here, and can make things clearer for you.
 
> 'void' doesn't restrict me from using it with user-defined types too. I
> feel I'd sometimes want an un-initializing constructor, distinct from
> the default constructor.
 
Apparently D can use "void" like this (I have no actual experience with
D, just a little knowledge).
 
It is possible to make an approximate std::indeterminate implementation,
at least using compiler-specific code, but doing so properly (in a way
that gives the compiler maximal freedom) would take compiler support.
 
> you have to ask for that or accept a well specified default state. I
> often find myself wishing the built-in types worked this way, almost
> always when adding new variables to classes.
 
Putting 5 ints into std::vector<int> would be a reasonable default
constructor - the main point is to make it consistent according to the
class's invariant. But it turns out that initialising the vector to
empty is the easiest and most efficient way to establish the invariant,
and it is convenient for everyone. So it is specified this way in the
standard - and we could complain if it didn't follow that standard.
 
However, there is nothing to guarantee that its /capacity/ is 0. I
haven't heard of any implementations that have an initial capacity other
than 0, but it would be legal.
flimflim1172@gmail.com: Dec 16 04:30PM -0800

On Wednesday, December 16, 2015 at 5:00:03 PM UTC-7, David Brown wrote:
 
> about it - and you must do that anyway. Having the compiler make a
> default that is sometimes appropriate risks letting you get away with
> not thinking - and that's a bad thing.
 
There are times when the code is flying faster than this. Sometimes days are spent on the same 100 lines (i.e. designing a lock free interaction), and sometimes 1000 lines are written in a matter of hours highly iteratively (i.e. writing a boss battle for a hop & bop), and because of the nature of the two systems you could well have more faith in the latter. It's that second type of example where sometimes I get weary of declaration, initialization, and implementation all being in different places.
 
 
> warnings show that you are doing something wrong. There are good for
> catching the occasional typo or glip, but if you get them often you
> should think about how you are writing your code.
 
I don't mean I'm somehow endlessly vexed by them. I mean I'm quite familiar with them, because over the years I've seen them enough to appreciate their existence. I've made a lot of typos and glips over my life.
 
 
> units (it might be able to do it with link-time optimisation). It can
> only hope to see the issue if you made your constructors inline along
> with the class definition.
 
Agreed. Again, this is the case that motivates me. While I appreciate static compiler analysis, this is why I don't feel satisfied by it.
 
 
> empty is the easiest and most efficient way to establish the invariant,
> and it is convenient for everyone. So it is specified this way in the
> standard - and we could complain if it didn't follow that standard.
 
I think I see your point. It's not just that the invariants are satisfied, but that they are satisfied most efficiently. That might be the key consistency link I've been searching for to rationalize the current state of things.
 
 
Sorry about messing up the attributions. I kept yours at the top of this email. I don't do newsgroup postings - ever - not sure what possessed me today!
Paavo Helde <myfirstname@osa.pri.ee>: Dec 17 02:05AM -0600

David Brown <david.brown@hesbynett.no> wrote in
 
> Ask yourself /why/ you are adding x_ to the class. What is it doing?
> Of course that means editing the constructor
> - adding the member to the class definition is only half the job.
 
It becomes worse if there is not one, but many constructors (C++11
forarding constructors would help here, BTW), plus the copy constructor,
assignment, move constructor, move assignment, swap function.
 
It is even worse when the new member needs to be initialized only in some
cases, depending on the other values. Just a couple of days ago I added a
new bool field to a low-level critical-speed variant-type class which had
ca 12 constructors, 4 of which required initialization of the new field.
Plus the copy/move ctor/assignment, plus swap. No fun, I can tell.
 
Cheers,
Paavo
David Brown <david.brown@hesbynett.no>: Dec 17 01:13PM +0100

(As Ian says, please don't snip attributions. Also try to get your line
wrapping in order. I see you are using the Google Groups interface,
which is the worst Usenet client around - you have to go to some effort
to follow standard Usenet conventions. If you are going to use Usenet,
I strongly recommend you get a proper newsreader and server. Unless you
have particular needs, a good and free combination is Thunderbird
newsreader with news.eternal-september.org as the server.)
 
> have more faith in the latter. It's that second type of example where
> sometimes I get weary of declaration, initialization, and
> implementation all being in different places.
 
That is no excuse. Write the code correctly, in the correct place. If
you are coding so fast that it is an effort to track initialisations,
you are coding too fast - it is much better to take the time needed to
write code correctly first, than to spend more time trying to debug it
later.
 
If it is more convenient for you, then you might consider changing your
style a little to put the initialisation in the class declaration rather
than the class implementation file - then you don't have to jump around
between two parts of the code. (Of course, a reasonable IDE will let
you have both files open at the same time - keeping them consistent
should not be difficult.)
 
 
> Agreed. Again, this is the case that motivates me. While I
> appreciate static compiler analysis, this is why I don't feel
> satisfied by it.
 
Static analysis is an aid to spotting mistakes - it is not an automatic
code reviewer.
 
> satisfied, but that they are satisfied most efficiently. That might
> be the key consistency link I've been searching for to rationalize
> the current state of things.
 
The key to initialising an object or variable is that it must be put in
a valid self-consistent state. If no specific value is given, then in
should be initialised in the most efficient way to establish consistency
- it is then up to the user of the object to put a useful value into it.
 
 
> Sorry about messing up the attributions. I kept yours at the top of
> this email. I don't do newsgroup postings - ever - not sure what
> possessed me today!
 
You kept my attribution, but snipped the others. As long as there are
quotations in the post, the attributions at the top should show where
they cam from - thus the attribution list should match the maximum depth
of the nested quotations. (It's okay if there are a few extra
attributions extra, or if there is a little mismatch on deeply quoted
messages - but the last few attributions at least should be correct.)
Bo Persson <bop@gmb.dk>: Dec 17 05:11PM +0100

> Bar bar = std::undefined; // compiler error
 
> I know there's at least a few of us out there who would welcome this sort of thing. I also know there's massive opposition to this from really smart people, and so I want to learn from you. If I could understand why it's better for built-ins to be undefined by default rather than by request, it would be a great help to me.
 
> Please keep in mind I'm asking for guidance completely separate from the issues of legacy code or precedent set by C. I understand many of those issues. I instead want to learn why it shouldn't be done even if C++ were being designed for the first time today. If I could get that through my thick head, I'd be very happy.
 
But is HAS to do with C compatibility. And performance.
 
If C++ were to initialize all C compatible types, the internet would be
full of benchmarks showing how crappy C++ is and that C runs circles
around it:
 
void f()
{
int x[10000000];
}
 
 
"How come safe-C++ runs this simple function a million times slower than
a C program?"
 
 
 
Bo Persson
mark <mark@invalid.invalid>: Dec 17 05:39PM +0100

On 2015-12-17 00:59, David Brown wrote:
 
> about it - and you must do that anyway. Having the compiler make a
> default that is sometimes appropriate risks letting you get away with
> not thinking - and that's a bad thing.
 
I strongly disagree with you. 95% of the time, 0-initialization would be
a sane thing to do. If you have several constructors, it's way too easy
to forget one (at least with C++11, you now have delegating constructors
and in-class member initializers).
 
What's worse, things may work fine depending on compile / optimization
settings, where you end up with 0-initialized memory, so this is easy
for tests to miss.
 
Undiagnosed non-deterministic behavior is really, really bad.
flimflim1172@gmail.com: Dec 17 10:05AM -0800

On Thursday, December 17, 2015 at 5:14:28 AM UTC-7, David Brown wrote:
> you are coding too fast - it is much better to take the time needed to
> write code correctly first, than to spend more time trying to debug it
> later.
 
It's not an excuse, it's a reality of a certain subset of code that I write. The problem spaces that you and I work in apparently don't intersect here. "Tracking" initialization is not the problem.
 
C++11 does indeed allow me to initialize at the point of declaration for members, which would help the convenience issue in those fast/high-volume coding situations. In my case though, I'm still handcuffed to C++03 for reasons outside my control.
 
It really helps a lot to have a consistent rationale for why int and vector<int> are not so different in how they default construct - Thankyou! I've definitely learned something valuable with this thread. Still though, I feel there's a subset of code that I write where knowing that int's invariants are satisfied at construction is not as good as knowing that it has either a predictable value or an explicitly requested unspecified value.
 
I'm think I'm realizing that my long-held approach of using pure C++ for all levels of application development needs to budge. I've really been a stick in the mud on this, but I'm inspired now to try mixing in D or Rust for certain parts of my applications where those languages might fit better.
flimflim1172@gmail.com: Dec 17 10:08AM -0800

On Thursday, December 17, 2015 at 9:12:22 AM UTC-7, Bo Persson wrote:
 
> "How come safe-C++ runs this simple function a million times slower than
> a C program?"
 
> Bo Persson
 
void f()
{
int x[10000000] = { void }; // would be just as fast
}
asetofsymbols@gmail.com: Dec 17 10:25AM -0800

int x[10000000] = { 0 };
David Brown <david.brown@hesbynett.no>: Dec 17 10:27PM +0100

> for members, which would help the convenience issue in those
> fast/high-volume coding situations. In my case though, I'm still
> handcuffed to C++03 for reasons outside my control.
 
If you have the freedom to consider D or Rust, then surely you also have
the freedom to move to C++11? C++11 is a significantly better language
than C++03 in many ways, and is worth pushing for.
 
> where knowing that int's invariants are satisfied at construction is
> not as good as knowing that it has either a predictable value or an
> explicitly requested unspecified value.
 
It really isn't hard to write "int x = 0;" in the few occasions where
you have a local int variable that you don't want to initialise to a
particular value, yet want to initialise to /something/. I realise it
looks somewhat inconsistent, but C++ has always had a distinction
between "plain" data types and object types.
 
> really been a stick in the mud on this, but I'm inspired now to try
> mixing in D or Rust for certain parts of my applications where those
> languages might fit better.
 
To my mind, there is not much difference between C++ and D and Rust -
they cover a similar application arena. There are certainly types of
application where C++ is not the most efficient development language -
but changing to something very different, such as Python, is likely to
have much more impact.
David Brown <david.brown@hesbynett.no>: Dec 17 10:34PM +0100

On 17/12/15 17:39, mark wrote:
> settings, where you end up with 0-initialized memory, so this is easy
> for tests to miss.
 
> Undiagnosed non-deterministic behavior is really, really bad.
 
Automatic zero initialisation of "int" (and other PODs) does two things.
One is that it gives you a specific known value, that may or may not
be of any use. Two is that it removes any chance the compiler has of
telling you that you haven't actually initialised it to anything useful.
 
So my recommendation is to initialise your variables in the correct way
for the code - don't put in dummy initialisations that disable helpful
warnings, and perhaps mislead the reader.
Wouter van Ooijen <wouter@voti.nl>: Dec 17 08:19PM +0100

Op 16-Dec-15 om 10:17 PM schreef Scott Lurndal:
 
>> I referred to Cortex-M, which is IMO a modern architecture too, although
>> not for desktop use.
 
> Modern in the sense that it was designed in 1983-85 for the Acorn Risc Machine (ARM)?
 
Cortex indeed descends from the original ARM, like the current Intel
iWhathever's decend from the 4004.
 
Wouter
scott@slp53.sl.home (Scott Lurndal): Dec 17 07:51PM


>> Modern in the sense that it was designed in 1983-85 for the Acorn Risc Machine (ARM)?
 
>Cortex indeed descends from the original ARM, like the current Intel
>iWhathever's decend from the 4004.
 
I find your comparison wanting. The 4004 ISA is nothing like the 8086/8088
and its decendents. Whereas the ARMv1 (Acorn) through the ARMv7 (Cortex)
architectures have basically the same ISA, with additions
over time (e.g. thumb, jazelle, thumbEE). ARMv8 was the first major software visible
architectural change (from 32-bit to 64-bit) (and thankfully, they
finally eliminated predicated instructions, at least in the 64-bit ISA).
David Brown <david.brown@hesbynett.no>: Dec 17 10:21PM +0100

On 17/12/15 20:51, Scott Lurndal wrote:
> over time (e.g. thumb, jazelle, thumbEE). ARMv8 was the first major software visible
> architectural change (from 32-bit to 64-bit) (and thankfully, they
> finally eliminated predicated instructions, at least in the 64-bit ISA).
 
The Cortex M does not have predicated instructions either (though it has
an "if-then-else" instruction). The Thumb2 instruction set has a number
of differences from the original ARM instruction set (though it is
mostly similar), and the internal architecture of the Cortex-M is a
modern design. The evolutionary gap between the Cortex-M and the
original ARM is not nearly as large as between the 4004 and an i7, but I
don't think it is unreasonable to view the Cortex-M as a modern (or at
least, modernised) design.
ram@zedat.fu-berlin.de (Stefan Ram): Dec 17 06:16PM

>I'm still handcuffed to C++03 for reasons outside my control.
...
>budge. I've really been a stick in the mud on this, but I'm
>inspired now to try mixing in D or Rust for certain parts of
>my applications where those languages might fit better.
 
There are two apparent contradictions here:
 
You say that you are handcuffed to C++03 and that you are
using »pure C++«.
 
But C++03 is not C++. The C++ norm says:
 
»This fourth edition cancels and replaces the third
edition (ISO/IEC 14882:2011), of which it constitutes.«
 
And before ISO/IEC 14882:2011 said:
 
»This third edition cancels and replaces the second
edition (ISO/IEC 14882:2003), which has been technically
revised.«
 
You are not using »pure C++«, you are using C++03.
C++03 is not C++.
 
You say that you are handcuffed to C++03 and your are
now inspired to try to mix in D or Rust.
 
But if you are /handcuffed to C++03/, you can't use D
or Rust.
ram@zedat.fu-berlin.de (Stefan Ram): Dec 17 06:34PM

>»This fourth edition cancels and replaces the third
>edition (ISO/IEC 14882:2011), of which it constitutes.«
 
s/tes\./tes a minor revision./
ram@zedat.fu-berlin.de (Stefan Ram): Dec 17 07:45PM

>The weird thing is that if I uncomment those print
>statements, then the function starts working!
 
When floating values are compared with »<«, small deviations
can change the result. I can imagine that optimization might
change the way floating point numbers are treated in some cases.
 
Introducing consts, you can analyze expression without slowing
down the program, provided a reasonable optimizer is used.
 
Using this technique, you can possibly see exactly which operation
first yields a different outcome when going to the release build.
 
/* untested */
 
#define PRINT(x) ::std::cerr<<#x<<" = "<<x<<'\n';
 
int point_in_polygon
( const float testx,
const float testy,
const Fpoint * points,
const int nvert )
{ int c = 0;
 
for( int i = 0, j = nvert - 1; i < nvert; j = i++ )
{
int const left = points[ i ].y > testy;
int const right = points[ j ].y > testy;
int const is_not_equal = left != right;
float const multiplicand = testy - points[ i ].y;
float const multiplicator = points[ j ].x - points[ i ].x;
float const product = multiplicand * multiplicator;
float const divider = points[ j ].y - points[ i ].y;
float const quotient = product / divider;
float const limit = quotient + points[ i ].x ;
int const is_below = testx < limit;
int const it_is_true = is_not_equal && is_below;
 
PRINT(left);
PRINT(right);
PRINT(is_not_equal);
PRINT(multiplicand);
PRINT(multiplicator);
PRINT(product);
PRINT(divider);
PRINT(quotient);
PRINT(limit);
PRINT(is_below);
PRINT(it_is_true);
 
if( it_is_true )c = !c; }
 
return c; }
woodbrian77@gmail.com: Dec 17 09:12AM -0800


> Would others like to join me in making suggestions on how
> to change that page? As Geoff mentioned, the order could
> be alphabetical. That would be okay with me.
 
The oppression of C++ by Java continues on that page.
 
Brian
Ebenezer Enterprises
http://webEbenezer.net
Lynn McGuire <lmc@winsim.com>: Dec 16 07:03PM -0600

"Modern C++ Features – override and final"
http://arne-mertz.de/2015/12/modern-c-features-override-and-final/
 
"Today I write about a pair of less often discussed, less complicated features introduced in C++11, which are nevertheless useful.
Both can provide some additional security and clarity when it comes to deriving classes and overloading virtual functions."
 
Nice! Makes one scared to use const without painstakingly ensuring that you are consistent.
 
Lynn
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: