From: Thomas Richter on 4 Mar 2010 06:18 Daniel T. wrote: > Gerhard Menzl <clcppm-poster(a)this.is.invalid> wrote: >> Daniel T. wrote: >> >>> I am pretty dogmatic about the single exit principle. I've had too >>> many times when I was trying to find some hard to track bug in >>> someone else's code only to learn that it had to do with some early >>> return or continue that bypassed a critical piece of code. >> So you're using C++ without exceptions then? > > I don't use exceptions for normal control flow. > > In my experience (and it might be just in my domain,) if an exception is > thrown, then it is because a programmer made an unwarranted assumption > while writing some particular piece of code and the code should be fixed > so the exception is no longer thrown. Huh, you must be working then in a rather unusual domain. What you describe here sounds like a job for assertions, not exceptions. I use exceptions to report invalid input, user errors, etc... So long, Thomas -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Balog Pal on 4 Mar 2010 06:20 "Daniel T." <daniel_t(a)earthlink.net> >> You skipped over the my mentioning exceptions. As exceptions can >> happen, it is no longer just a style, and if one thinks some layout >> *has* the actual SESE property just by restricting to a single return >> -- is quite mislead. Thus imposing danger. > > Here too, I will make another attempt at explaining myself. You have > brought up several points and I will attempt to cover them all, but the > last item is IMHO the most importnat so I will cover it first. > > _Exceptions to the Rule_ > > Of course there are exceptions to every rule (including this one. :-) As > programmers we have lots of different design goals and many of them call > for mutually opposing solutions, coding is often about trade-offs. It is > not wrong of me to avoid multiple exits when there are no important > design or performance constraints requiring them. To put it another way, > there is no design rule that says "prefer multiple exits from blocks of > code," yet that is what you seem to be advocating. (If I am wrong here, > then we are probably not that far apart in our positions.) :-( I definitely mentioned 'exceptions' as in the C++ thingy related to throw/try/catch and in that meaning. And after the second iteration it is unclear how it can be not an obvious and serious problem to a C++ programmer, looking at the discussed situation and reasoning. > I can see where this issue might have stemmed from the use of the word > "dogmatic." I said "pretty dogmatic" and you might have thought that I > meant that I insist on single returns no matter what the situation. Well, I guess that is the vocabulary meaning of dogmatic ;-) > That > is not exactly what I meant, rather I meant that I am more willing to > expend the extra effort to find a single return solution than most other > programmers. That sounds much better. Yet still not at the optimum. Why it is good to put extra effort to shape a piece of code against its natural way? Really. (As when single exit is natural, no effort is needed, and when it is not, then you fight to reach a suboptimal shape, do you?) I am certainly not advocating to create extra exits, or multiple ones just for fun, but advocating to use exactly the amount that is good. Let it be one, two or 42. Not even thinking to go into statistics which is more frequent, as it is irrelevant, all that counts is the actual case you deal with. > Hopefully the above clears up this issue, but if not then continue > reading... > > _Classic Example, Finding Something in a Collection_ > > Your "classic example" is IMHO a very good one. When we search for a > thing in a collection, should we prefer multiple exits? I think not. No, we should not prefer anything up front before facing the code. > This means approaching the problem in a different way, but it doesn't > necessarily entail what you called a "framework of flags or other > obfuscation measures." Yes, it is possible to have special situations. > If we approach the problem from the assumption > that multiple exits are to be preferred, then we might write it like > this: > > template< typename InIt, typename Tp> > InIt find(InIt first, InIt last, const Tp val) { > for (InIt it = first ; it != last; ++it) > if (*it == val) > return it; > return last; > } > > If we try to avoid multiple returns we might end up with something like > this (cribbed from GCC's library): > > template< typename InIt, typename Tp> > InIt find(InIt first, InIt last, const Tp val) { > while (first != last && !(*first == val)) > ++first; > return first; > } Wow, I see you carefully selected those special situations. Now how about picking some general example, that does not have such special property with the return value. Say you search in a vector and supposed to return the index (0 to size()-1) or some special value npos when not found. (similar to string::find()). Or search a list and return the address of the found value -- or NULL if not there. The important point being that the special not-found value does not have any relation with anything you naturally use as the iterator. > Now you probably would not write the multiple return example exactly as > I did, but however you write it, I don't think that the multiple return > example should be preferred as a design rule. What was never stated -- as we know the implication is not invertable that way. The design rule is to create correct code that is also easy to read, review, modify, etc. Using either a SESE or SEME shape is no goal but a means to reach it. You chose the solution fitting the probem instead chasing application of a stock "solution". > I am even willing to add a > "result" variable to avoid multiple returns, but I'm not going to throw > in a bunch of flags, that would be silly. Yes, addig a "result" variable and assigning it at several places is the common BAD practice that follows from chasing SESE where it is not at all necessary. > (As a side note, the GCC library has a separate 'find' for use with > random access iterators that does use multiple returns and looks > remarkably like a somewhat cleaner version of Duff's Device. This is > obviously a performance optimization, which is the one thing that trumps > design near the end of a product's construction.) Looking at Dinkum's basic_string::find implementation, it has 3 returns, having a pretty natural natural layout, like if( needle is empty && startpos is inside) return startpos; iteration( ... ) { ... return pos where match found } return npos; // no match Can it be forced to SESE? Sure. Would it be better? i keep my reservation until shown a really elegant alternative. > _Postconditions and Where to Check For Them_ > > Again we come back to not repeating ourselves. If the post condition of > a function can be checked by code, then it should be. I think we can > agree on that, but where should we do the checking? Your suggestion was > that this checking should be done "at a caller site (as normally done in > unit tests)." > > Most functions are complex enough that they require multiple tests, so > they are generally called from multiple sites. If we want to put the > postcondition checking code in only one place (so we don't repeat > ourselves,) then the obvious place to put it is just before the return > within the function. You mean you want to mix the testing code right inside the production code? And keep the production code textually instrumented like an intermediate file of Coverity or a similar tool? Hopefully others read this topinc and can comment whether they do it or not... Also, "before return" is not a realistic place for (I'd guess the majority) of the non-void functions, as return will have some expression that is yet to execute as part of it. So you'd have to significantly cripple the code (about to the level of making the best tool: reviews infeasible). Bad trade. Where quality works, people write literary code banning any clutter, and run non-intrusive tests. (Or if anything intrusive is needed do it through some generator.) > If there is more than one return, then we still > must repeat ourselves; which is the one thing that we have both agreed > we should avoid. That is not true, as the separate returns actually match a different portion of the postcondition, so there is nothing to repeat. Like in the previous find() example, we have separate cases for disjunct parts of the requirement. If you want to check them inside, you need only part of the condition. While checking outside is just a long list of input/expected output pairs written in black-box manner. > Hopefully this post will clear up my thinking about these issues and > cover any concerns or confusion you may have. Well, it did cover some points, reveled one miss and some gaps. -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Gerhard Menzl on 4 Mar 2010 17:59 Daniel T. wrote: >> > I am pretty dogmatic about the single exit principle. I've had too >> > many times when I was trying to find some hard to track bug in >> > someone else's code only to learn that it had to do with some early >> > return or continue that bypassed a critical piece of code. >> >> So you're using C++ without exceptions then? > > I don't use exceptions for normal control flow. Nevertheless, you get multiple points of exit anyway. It suffices to write: std::vector<C> v(c_begin, c_end); Therefore single exit is an illusion anyway. Which does, of course, not mean that you shouldn't care about well-structured code. It's just that being dogmatic about single exit can lead to more convoluted code than a more pragmatic approach. > In my experience (and it might be just in my domain,) if an exception is > thrown, then it is because a programmer made an unwarranted assumption > while writing some particular piece of code and the code should be fixed > so the exception is no longer thrown. As others have pointed out, this is what assertions are for. -- Gerhard Menzl Non-spammers may respond to my email address, which is composed of my full name, separated by a dot, followed by at, followed by "fwz", followed by a dot, followed by "aero". [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Nevin :-] Liber on 4 Mar 2010 17:55 In article <memo.20100304194830.9448A(a)brangdon.cix.compulink.co.uk>, brangdon(a)cix.co.uk (Dave Harris) wrote: > Personally I'd see it as an example of a lexical compare, which I usually > write: > if (lhs.part_a != rhs.part_a) > return lhs.part_a < rhs.part_a; if (lhs.part_b != rhs.part_b) > return lhs.part_b < rhs.part_b; > if (lhs.part_c != rhs.part_c) > return lhs.part_c < rhs.part_c; > // ... > return false; // lhs == rhs. I tend to make my version only dependent on operator<, as in: if (lhs.part_a < rhs.part_a) return true; if (rhs.part_a < lhs.part_a) return false; if (lhs.part_b < rhs.part_b) return true; if (rhs.part_b < lhs.part_b) return false; // if (lhs.part_c < rhs.part_c) return true; // if (rhs.part_c < lhs.part_c) return false; // return false; // // replace the the three lines with return lhs.part_c < rhs.part_c; Or, written more succinctly using the Tuples library: return boost::tie(lhs.part_a, lhs.part_b, lhs.part_c) < boost::tie(rhs.part_a, rhs.part_b, rhs.part_c); -- Nevin ":-)" Liber <mailto:nevin(a)eviloverlord.com> 773 961-1620 [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ]
From: Juan Pedro Bolivar Puente on 4 Mar 2010 21:24
> > I think others have largely answered the question you asked by now - but > this question did strike me as as good a time as any to use a nice > linear-time counting sort. Make a small array, accumulate the number of > times each letter appears, then output them in the right order. Things > like quicksort are overkill for this. As an added bonus, counting sort > is easier to implement too (if you're doing it from scratch, anyway). > Good point :) Still that solution would have to run over all the character set (or the subset that we are considering), so a traditional n-log sort is better if we expect most of the input strings to be relatively small. It seems that there is no one-size-fits-all... JP -- [ See http://www.gotw.ca/resources/clcm.htm for info about ] [ comp.lang.c++.moderated. First time posters: Do this! ] |