- Use of the break; and continue; statments - 14 Updates
- compiler generate jmp to another jmp - 1 Update
- linker error - 2 Updates
- Use of the break; and continue; statments - 4 Updates
- neoGFX wiki - 2 Updates
- "Need for Speed - C++ versus Assembly Language" - 2 Updates
Bob Langelaan <bobl0456@gmail.com>: May 17 09:06PM -0700 I teach a beginner C++ course and have taught my students that they should minimize the use of the break; and continue; statements. Recently I have been getting push back from some of my students with statements like "my friends at Microsoft tell me they use break; statements all the time" and similar types of objections. I created the post below for my students on this topic some time ago. I would be interested in getting feedback on my post and the topic in general. Thanks in advance, Bob =============================================================================== Many beginner programmers will unfortunately use the break statement and the continue statement a great deal more than they should. I will go further and say that, generally speaking, a programmer should try to limit the use of break statements to exiting from a switch statement. And that the continue statement should almost never be used. Here are examples of using the break statement and continue statement that should normally be avoided: // An example of poor use of the break statement while (exit_criteria not met) { . . . if (exit_criteria_met) { break; // exit while loop immediately } . . . } // end of while loop // An example of poor use of the continue statement while (exit_criteria not met) { . . . if (exit_criteria_met) { continue; // Go immediately to the end of the while loop. // Loop will exit with next iteration because // the exit_criteria has been met. } . . . } // end of while loop In the 2 examples above, the exit_criteria is tested twice for each iteration of the while loop. This is considered both inefficient and poor programming style. The following example is also considered poor programming style because you should be exiting a loop at the beginning of the loop or at the end of the loop, and not in the middle of the loop: // Another example of poor use of the break statement while (true) // The same as "while (1)" , but I prefer the former { . . . if (exit_criteria_met) { break; // exit while loop } . . . } // end of while loop Using as an example a portion of the solution to exercise 4.13 of the text, here is how you can avoid the 3 poor examples above: // prompt the user for the first value for miles cout << "Enter the miles used (-1 to quit): "; cin >> miles; // Exit the loop if the input is -1, otherwise proceed with // the next iteration of the loop. while ( miles != -1 ) { // prompt user for gallons and obtain the input from user cout << "Enter gallons: "; cin >> gallons; . . // Complete calculations using current values . // input for miles and gallons // prompt user for next value for miles cout << "\n\nEnter the miles used (-1 to quit): "; cin >> miles; } // end while The example above is a pattern that is used very commonly in well crafted code. You will note that it contains _NO_ break statements or continue statements, that there is only one test to exit the loop, and that this one test is done at the beginning of the loop. Some of you will notice the solution above requires some code duplication. The code that inputs the miles is duplicated - once before entering the while loop and again at the end of the while loop. While we generally try to avoid code duplication, in this case, this code duplication is preferred over having an unnecessary break statement or continue statement in the loop. In other words, in this case it is considered the lessor of two evils :) I will occasionally use a break statement in a situation similar to the next example: // In the example below we are searching for a value in an array bool found = false; for (unsigned int value ; I < ARRAY_SIZE; ++i)// iterate through the array { if (myArray[i] == search_value) { found = true; break; // exit loop since we have found search_value } } // When we reach this point, "found" will be true if search_value was // found in the array and false otherwise. I tutor students from schools all over the lower mainland on C/C++. Several of these students have told me in the past that their instructors do not allow _ANY_ use of break statements, other than in a switch statement. So how would you rewrite the previous example without using a break statement, but still be equally efficient? Quite simply actually: bool found = false; // iterate through the array for (unsigned int value ; (!found) && (i < ARRAY_SIZE); ++i) { if (myArray[i] == search_value) { found = true; } } // When we reach this point, "found" will be true if search_value was found // in the array and false otherwise. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: May 18 07:27AM +0200 On 18-May-17 6:06 AM, Bob Langelaan wrote: > I teach a beginner C++ course and have taught my students that they > should minimize the use of the break; and continue; statements. Good advice. :) > I created the post below for my students on this topic some time ago. > I would be interested in getting feedback on my post and the topic in > general. Well, the concrete advice on rewriting a loop-and-a-half, for( ;; ) { int value; cout << "Value (0 to quit? "; cin >> value; if( value == 0 ) { break; } // Do computations, present result. } as int value; cout << "Value (0 to quit? "; cin >> value; while( value != 0 ) { // Do computations, present result. cout << "Value (0 to quit? "; cin >> value; } is ungood, downright evil, because 1. it introduces undesirable redundancy, repeated code. Not doing that is known as the DRY principle: Don't Repeat Yourself. Stick to DRY. 2. It needlessly expands the scope of the support variables. And if that scope is contained via an outer curly braces block, then it needlessly adds extra indentation. You say about it: > duplication is preferred over having an unnecessary break statement or > continue statement in the loop. In other words, in this case it is > considered the lessor of two evils :) No, it's not the lesser of two evils. It's an evil introduced to avoid perfectly good ordinary code because you have OVER-GENERALIZED a rule about avoiding `break`. You have made it an absolute rule, which is easy to follow mechanically but produces rather sub-optimal results, instead of a guideline that requires intelligence in its application. Instead IMO you should teach your students about loop-and-a-half: how to recognize that it's logically there (code duplication) and how to express it properly (with a break or return or throw in the middle). • • • That said, your students' argument about `break` being used at Microsoft is not a valid argument. Very seldom do you find as dirty and invalid code as at Microsoft. To wit, their documentation has a host of instances of invalid `void main`. Not to mention the Microsoft monstrosities such as `tmain`. These are beginner mistakes. And they're all over the place at MS. Cheers & hth., - Alf |
Christian Gollwitzer <auriocus@gmx.de>: May 18 08:13AM +0200 Am 18.05.17 um 07:27 schrieb Alf P. Steinbach: > No, it's not the lesser of two evils. It's an evil introduced to avoid > perfectly good ordinary code because you have OVER-GENERALIZED a rule > about avoiding `break`. +1. Upon reading this paragraph, I thought it to be satirical, but from the following it appears that you mean it as written. It is neither good to repeat the loop condition, nor to repeat the code, but repeating more than one line of code as a trade-in for not repeating the loop condition nor using break sounds absurd. Break is there for a reason. The alternative would be to set a condition flag, not to repeat the code. I prefer break over condition flags if it can be clearly expressed that way. Christian |
Christian Gollwitzer <auriocus@gmx.de>: May 18 08:14AM +0200 Am 18.05.17 um 08:13 schrieb Christian Gollwitzer: >> about avoiding `break`. > +1. > Upon reading this paragraph, I thought it to be satirical, I meant the OP's paragraph, just to make that clear ;) Christian |
bitrex <bitrex@de.lete.earthlink.net>: May 18 02:30AM -0400 On 05/18/2017 12:06 AM, Bob Langelaan wrote: > found = true; > } > } bool found = false; for (size_t i = 0; i < ARRAY_SIZE; ++i) { if (!found) { found = (myArray[i] == search_value); } else break; } Break statements are cool |
Ian Collins <ian-news@hotmail.com>: May 18 06:34PM +1200 On 05/18/17 04:06 PM, Bob Langelaan wrote: > I teach a beginner C++ course and have taught my students that they > should minimize the use of the break; and continue; statements. Good advice with an emphasis on "minimise", they sometimes have their place. Most places where beak is used in a loop can be replaced with a return if the loop is extracted into a function :) -- Ian |
David Brown <david.brown@hesbynett.no>: May 18 09:36AM +0200 (/Please/ get a proper newsreader. It is nearly impossible to deal with google-groups line split crap without making a mess.) On 18/05/17 06:06, Bob Langelaan wrote: > I have been getting push back from some of my students with statements > like "my friends at Microsoft tell me they use break; statements all the > time" and similar types of objections. /Minimizing/ the use of "break" is good - obsessing about it or banning it is not. I find "continue" to be very rarely useful - I could happily live without it in the language. But sometimes "break" really is the best way to structure a loop. > I will go further and say that, generally speaking, a programmer > should try to limit the use of break statements to exiting from a switch > statement. And that the continue statement should almost never be used. That paragraph does not read well. I first thought you had said that "break" should not be used to exit switch statements either. > . > . > } // end of while loop It is rare that you want to have the same test twice - but not /always/ bad. It is likely that in a case like this, you could remove the condition at the start, making it a "while (true)" loop. In a "while (true)" loop, it is perfectly clear that the loop exit will be done somewhere in the middle of the loop (except for intentionally never-ending loops). > . > . > } // end of while loop "Continue" here is daft - it should be "break". > In the 2 examples above, the exit_criteria is tested twice for each > iteration of the while loop. This is considered both inefficient and > poor programming style. Don't claim that something "is considered" unless you can back it up with clear justifications. Provide references from the Google C++ style guide, or MISRA, or quotations from one of the C++ "greats". If you cannot, then be honest and write "I consider it...". And I don't think you can really claim it is inefficient, if the test condition does not require external calls - the compiler will probably be able to optimise that "continue" into a single jump to outside the loop, just like a "break". > The following example is also considered poor programming style > because you should be exiting a loop at the beginning of the loop or at > the end of the loop, and not in the middle of the loop: Who says? /Why/ do you think that is a good idea? This all comes across as very dogmatic to me. It looks like you want to avoid exits in the middle of a loop simply because that is a rule that /you/ were taught. It is fine to say that exits in the middle of a loop are often hard to follow, and make the flow of the code unclear. That is a /reason/ for minimising the use of "break" and "continue". Saying they should be avoided because it is "considered poor programming style" is not a reason - any more than saying that "friends at Microsoft use break" is a good reason to use them. > // Another example of poor use of the break statement > while (true) // The same as "while (1)" , but I prefer the former (Side issue - I agree that "while (true)" is the nicest way to write such loops. I dislike the use of "1" for a boolean, and I think the alternative "for (;;)" is ugly. But beauty is in the eye of the beholder.) > . > . > } // end of while loop Often that is a perfectly good way to structure a loop. When you see "while (true)" at the start of a loop, you are in no doubt that the exit condition comes in the middle of the loop somewhere. If you don't know that it is time to exit until the middle of the loop, then putting a "break" (or perhaps "return") there seems perfectly reasonable. Another situation where the use of "while (true)" and breaks can make sense is if the exit criteria is complex. It may be natural to put these at the beginning or the end of the loop, but it can be clearer to have multiple statements, with space for comments, rather than jamming everything into one exit expression. (There are, of course, alternative restructurings - such as re-factoring the conditions into a function.) So rather than: while (A && B && !C) { ... } consider: while (true) { if (!A) break; // Exit if A does not hold if (!B) break; // Exit if B does not hold if (C) break; // Exit when C is reached ... } > duplication is preferred over having an unnecessary break statement or > continue statement in the loop. In other words, in this case it is > considered the lessor of two evils :) Sometimes code duplication is unavoidable. In such cases, choose to duplicate the simplest, shortest, clearest code - not the major lines. Testing "(miles == -1)" is far simpler and "smaller" than "cin >> miles". Thus it is the test that should be duplicated, /not/ the action of reading cin. So while I agree that you may need to choose the lesser of two evils, I think you have got the answer completely wrong in this case. The best structuring here is very simple: while (true) { cout << "Enter the miles used (-1 to quit): "; cin >> miles; if (miles == -1) break; ... } Then there is /no/ duplication of either the important statements, or the test. > } > // When we reach this point, "found" will be true if search_value was > // found in the array and false otherwise. Baring typos in the code (which are a /really/ bad idea in a post to students!), that is a perfectly good use of "break". > switch statement. So how would you rewrite the previous example without > using a break statement, but still be equally efficient? Quite simply > actually: The world is a big place, and this is an international newsgroup - no one can guess where "the lower mainland" is. I am always wary of hard and fast rules such as /never/ using break statements. I need justification - the stronger the rule, the more justification I need. And efficiency should not be your major concern here - code clarity and correctness trump efficiency every time. If you are going to have a rule about code style, ask yourself: Does it make it easier to write correct code? Does it make it harder to write incorrect code? Does it make it easier to see that correct code is correct? Does it make it easier to see that incorrect code is incorrect? For the most part, pick efficient algorithms and let the compiler worry about the efficiency of the detail in the code. The compiler is not going to follow your structure of loops and breaks anyway. > found = true; > } > } In my book, the complex condition in the for loop makes it poor code - it is unclear. It is not as bad as using "i = ARRAY_SIZE" to jump to the end of the loop, but it is still bad. This kind of exercise is a very artificial problem, and there are no nice answers. They can be useful while learning, but that does not make them good style in practice. |
"Fred.Zwarts" <F.Zwarts@KVI.nl>: May 18 10:00AM +0200 "bitrex" schreef in bericht news:KbbTA.89595$rl4.68345@fx35.iad... > if (!found) { found = (myArray[i] == search_value); } else break; >} >Break statements are cool Matter of taste. I would write: bool found = false; for (size_t i = 0; i < ARRAY_SIZE && !found; ++i) { found = (myArray[i] == search_value); } |
Cholo Lennon <chololennon@hotmail.com>: May 18 10:07AM -0300 On 18/05/17 08:53, Stefan Ram wrote: > int read_miles() > { int miles {}; > ::std::cout << "Enter the miles used (-1 to quit): "s; Why the use of the literal s? It's really unnecessary :-O -- Cholo Lennon Bs.As. ARG |
bitrex <bitrex@de.lete.earthlink.net>: May 18 09:49AM -0400 On 05/18/2017 04:00 AM, Fred.Zwarts wrote: > for (size_t i = 0; i < ARRAY_SIZE && !found; ++i) { > found = (myArray[i] == search_value); > } Years of Python has corrupted me I think. ;-) I don't like having multiple conditionals in for loops, I think it can be kinda confusing |
bitrex <bitrex@de.lete.earthlink.net>: May 18 09:53AM -0400 On 05/18/2017 09:49 AM, bitrex wrote: > Years of Python has corrupted me I think. ;-) > I don't like having multiple conditionals in for loops, I think it can > be kinda confusing Under gcc 7.1 x86 with -std=c++11 -O1 FWIW both our variations compile to the same sequence of instructions. |
bitrex <bitrex@de.lete.earthlink.net>: May 18 10:08AM -0400 On 05/18/2017 08:14 AM, Stefan Ram wrote: > that without SESE it can be much more difficult to > apply an "extract method" refactor or to add something > that should be done on every entry to or exit of a block. If one's methods are so bloated that one can't pretty much immediately see where the exit points are then they need to go on a diet... |
bitrex <bitrex@de.lete.earthlink.net>: May 18 10:17AM -0400 On 05/18/2017 10:08 AM, bitrex wrote: >> that should be done on every entry to or exit of a block. > If one's methods are so bloated that one can't pretty much immediately > see where the exit points are then they need to go on a diet... Granted I'm a relative newcomer to C++; I never used the language "back in the day", I mostly have experience with straight C99, Python, and a little Lisp/Clojure. I've been trying to go straight to learning "modern C++" i.e. how things are done post C++11. I feel like big blocks of conditionals/if statements/switch statements are old 90s shit like what I was writing in C back then. I can't remember the last time I used a switch statement or multiple layers of if/else blocks in something I've written in C++...it feels too much like the "bad old days" and I figure I must be doing something wrong. |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: May 18 04:20PM +0200 On 18-May-17 9:36 AM, David Brown wrote: [snip] > with clear justifications. Provide references from the Google C++ style > guide, or MISRA, or quotations from one of the C++ "greats". If you > cannot, then be honest and write "I consider it...". Uhm, the Google C++ style guide is designed (if it is designed) for a C style legacy code base. It would not be a good idea to use it as an authority on anything C++. Don't know about MISRA but I suspect the same story: a special purpose thing. Cheers!, - Alf |
qak <q3k@mts.net.NOSPAM>: May 18 02:18PM David Brown <david.brown@hesbynett.no> wrote in > code to achieve the effect. In some operating systems (such as > Linux), code that is not executed on a particular platform might not > even be loaded off the disk when you run a program. Can individual instruction be 'hot', or only the whole block of code can be cached ? I always wish: if(AMD) do REP RET else do RET then after the first run all the first lines disappear from every PROC |
alexo <alessandro.volturno@libero.it>: May 18 01:26PM +0200 Il 17/05/2017 20:39, Paavo Helde ha scritto: > It should compile if you define the destructor and make data public. If > it does not, then you introduced some other error. Please post the new > code and matching error messages, we are not psychics here! I thought you were that ; ) > If you get to the linker errors, this means that at least the compiler > was satisfied with your syntax. Whether the code is actually correct and > does what you wanted is another story. It happened a strange thing. I put all the code in a single main.cpp file and it compiles, links and executes correctly. While having a MyClass.h and MyClass.cpp files whorking with a main function in a separate file, it gives those strange linker error messages. here is the correct code: #include <iostream> #include <typeinfo> #include <string> #include <stdio.h> using std::cout; using std::endl; using std::string; /// the class' declaration template <class T> class MyClass { public: T data; MyClass(); MyClass(T); MyClass(const MyClass &); }; /// the class' definition // constructor template <class T> MyClass<T>::MyClass(T val) { data = val; } template <class T> MyClass<T>::MyClass() { data = 10; } // copy constructor template <class T> MyClass<T>::MyClass(const MyClass<T> &obj) { if( typeid(T) == typeid(char *) ) { int len = strlen(obj.data); strncpy(data, obj.data, len+1); } else { data = obj.data; } } int main() { MyClass<string> test("THREE"); cout << test.data << endl; return 0; } |
Paavo Helde <myfirstname@osa.pri.ee>: May 18 04:19PM +0300 On 18.05.2017 14:26, alexo wrote: > executes correctly. > While having a MyClass.h and MyClass.cpp files whorking with a main > function in a separate file, it gives those strange linker error messages. Seems like https://isocpp.org/wiki/faq/templates#templates-defn-vs-decl |
ram@zedat.fu-berlin.de (Stefan Ram): May 18 11:36AM >my students that they should minimize the use of the break In your whole post I have searched for a /rationale/ (reason) for the above. I only found this so far: >In the 2 examples above, the exit_criteria is tested twice >for each iteration of the while loop. This is considered >both inefficient and poor programming style. This rationale is founded on DRY (don't repeat yourself, "tested twice"). Ok, we all agree on DRY. >style because you should be exiting a loop at the beginning >of the loop or at the end of the loop, and not in the middle >of the loop: I do not deem this a rationale because it is only founded on »because you should ...«, but then another rule is given which itself does not have a rationale. >duplication, in this case, this code duplication is preferred >over having an unnecessary break statement or continue >statement in the loop. Either code duplication is a major evil, than it can be used as a rationale for the avoidance of »break«, but then it also will dictate to avoid the »break«-less code given later. Or code duplication is acceptable, in which case your »break«-less code duplication is ok, but it now also is no rationale for the avoiding of break. And it still is to be disputed whether it is true at all that break will necessarily lead to code duplication or whether this is only the property of a constructed example. So far you have not given me a convincing rationale for the avoidance of »break«. Students find it hard to follow guidelines for which no convincing reasons are given. |
ram@zedat.fu-berlin.de (Stefan Ram): May 18 11:53AM > cout << "\n\nEnter the miles used (-1 to quit): "; > cin >> miles; >} My course participants have learned function definitions /before/ control structures. So they could avoid the definition using: #include <initializer_list> #include <iostream> #include <ostream> #include <string> using namespace ::std::literals; int read_miles() { int miles {}; ::std::cout << "Enter the miles used (-1 to quit): "s; ::std::cin >> miles; return miles; } int main() { int miles {}; while(( miles = read_miles() )!= -1 ) { /* ... */ }} However, I deem the whole field of input too difficult for my beginner's course and only treat it later in the advanced course. I usually do not mention »break;« at all and so I never saw it in the program of a participant IIRC. However, I do not avoid »break;« because I thing that ignorance will lead to a better programming style, but only because my beginner's course usually is too short and the advanced course has a different scope (user-defined types). |
ram@zedat.fu-berlin.de (Stefan Ram): May 18 12:14PM >I teach a beginner C++ course and have taught my students >that they should minimize the use of the break; It seems that you might not be aware of the whole background and only have heard of some guidelines yourself. The background is usually the (in)famous article »Go To Statement Considered Harmful« by Edgar Dijkstra. This lead to the school of »Structured Programming« (Dijkstra, Hoare, Dahl, Wirth, and others). There, you can find /reasons/ for /why/ every block should have exactly one entry (at the beginning) and exactly one entry (at the exit). SESE = single entry, single exit. And »break;« and »continue;« /are/ (restricted) forms of »goto«. One reason, I give to my participants for SESE is that without SESE it can be much more difficult to apply an "extract method" refactor or to add something that should be done on every entry to or exit of a block. In C++, however, one must also take into account that objects can be destroyd on /every/ path exiting a block, which leads to RAII. |
ram@zedat.fu-berlin.de (Stefan Ram): May 18 12:19PM >Well, the concrete advice on rewriting a loop-and-a-half, Kudos for introducing the proper term into the discussion! |
"Rick C. Hodgin" <rick.c.hodgin@gmail.com>: May 17 07:51PM -0700 +1. Awesome. You do good work, Leigh. Thank you, Rick C. Hodgin |
"Alf P. Steinbach" <alf.p.steinbach+usenet@gmail.com>: May 18 09:24AM +0200 On 17-May-17 2:05 AM, Mr Flibble wrote: > My upcoming C++ game/GUI library/framework "neoGFX" now has a wiki: > http://neogfx.io/wiki/index.php/Main_Page > neoGFX version 1.0 is about 75% complete at this stage. It looks good. Thanks for mentioning Vulcan, so I could learn about it. Cheers!, - Alf |
Jerry Stuckle <jstucklex@attglobal.net>: May 17 10:33PM -0400 On 5/17/2017 12:42 PM, Gareth Owen wrote: > Yup. The cost of speeding up the code with assembler (time, money, like > of portability) are far greater than just throwing a bunch more > processors at them Except that doubling the number of processors does NOT halve the time it takes to compute. Some computations cannot be started until previous computers have been completed. That is the case in most weather prediction algorithms. -- ================== Remove the "x" from my email address Jerry Stuckle jstucklex@attglobal.net ================== |
Jerry Stuckle <jstucklex@attglobal.net>: May 17 10:35PM -0400 On 5/17/2017 1:53 PM, Gareth Owen wrote: >> Christian > From the NOAA, not NASA or github. Not in assembly, either. > http://www.nco.ncep.noaa.gov/pmb/codes/nwprod/ Ok, so now let's see you compile and run the code successfully. You will find this is not the complete code for the models. -- ================== Remove the "x" from my email address Jerry Stuckle jstucklex@attglobal.net ================== |
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