- Weird release build bug - 5 Updates
- uninitialized build-in types - 12 Updates
- Dependecy Injection - 3 Updates
- uninitialized build-in types - 3 Updates
- C++ needs some help - 1 Update
- "Modern C++ Features – override and final" - 1 Update
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:
Post a Comment