|
In C++ an exception, there is no return in that subrutine.
The ANSI C equivalent may be a longjump, but this is even worst than a goto.
|
|
|
|
|
It's fine when it's the only tool in the box (very old BASIC, DCL, DOS batch files, etc.), but I've never needed it in languages that support structured programming.
|
|
|
|
|
There is few number of bad developers, like me, thinking the goto is not evil.
Veni, vidi, vici.
|
|
|
|
|
Getting past the denial stage is the first step towards a cure.
- I would love to change the world, but they won’t give me the source code.
|
|
|
|
|
You know, now I have to use the goto on you...
Veni, vidi, vici.
|
|
|
|
|
The book, "Classics In Software Engineering" has Dijkstra's excellent paper, "The Case Against The Goto". There was also another paper by another author in that book stating situations where the goto is useful. A goto is worthwhile in some very limited contexts.
I found a related very short paper by Dijkstra online that is titled, "A Card Against The Goto" at http://www.cs.utexas.edu/users/EWD/transcriptions/EWD02xx/EWD215.html[^]. It is more of a philosophical outlook about the issue and doesn't go into the depth of the article in the book.
It's also worthwhile reading the paragraphs under "Considered Harmful" at http://blogs.perl.org/users/erez_schatz/2011/07/rewriting-the-language.html[^]. That section tells about how when Dijkstra's article, "The Case Against The Goto" was submitted to the ACM magazine, the editor changed the title [to "Gotos Considered Harmful"] and created a furor! Two key sentences at the link above are:
"It should cease to exist. Nothing in programming is definite. There is no single element that is either a silver bullet or the Antichrist."
However, it is true that, in the vast majority of cases, using a goto can and should be avoided. But it's also a mistake to revile any code that contains a goto merely because the code contains one.
By the way, I also agree with another poster that multiple returns in a function are undesirable, although I can see exceptions for this too. A single return in a function makes debugging so much easier. I make a serious effort to have only a single return, however, I have broken this guideline at times, particularly when working on critical legacy code where I wanted to minimize changes to the code.
Here's a construct in pseudo-code that I've used in both C and C++ programs to avoid the need for gotos for multiple error cases. (By the way, I also always put the parenthesis in a statement, even for only one-line statements, because it makes the code easier to maintain. Typically, 85% of the cost, or time, spent on code is maintenance, so typically, code should be written to make it easy to maintain, as opposed to making it easy for the person writing the code).
I did not invent this technique and I don't know who to credit for this idea.
do
{
if (error)
{
break;
}
if (error)
{
break;
}
if (error)
{
break;
}
}
while (false);
That construct avoids the extreme indenting that can occur with a lot of nested error checks. Each 'break' statement acts like a goto that sends control to the first statement following the end of the do loop. And, of course, it's not really a loop at all because of the "while (false)" at the end. I believe that most compilers will optimize the loop away.
modified 10-Nov-13 12:51pm.
|
|
|
|
|
I prefer:
if (!error)
{
if (!error)
{
if (!error)
{
}
}
}
No "do...while" required.
This also has the advantage that excessive indenting reminds the programmer that they need to break the code out into method calls to simplify the layout. Once it gets past four or five indents this becomes obvious.
Better would be:
if (error)
{
}
else
{
if (error)
{
}
else
{
if (error)
{
}
else
{
}
}
}
This way each error can be reported as necessary, perhaps with some cleanup or roll-back code.
- I would love to change the world, but they won’t give me the source code.
|
|
|
|
|
That code is you showed is fine, and I also prefer the the nested form of error checking when it is manageable. That isn't always the case. (Note the comment in my last message, there is no "silver bullet!")
As I wrote in my code example, "That construct avoids the extreme indenting that can occur with a lot of nested error checks." The key words are "can" and "nesting". Deep nesting cannot always be easily avoided by breaking into functions, and when that is the case, the loop construct is useful.
For the example below I used the nested form to compare 8 keys, where the values of the 8 keys form a single key to a dictionary (or a map). Each item in the map is sorted in lexicographic order. (This code snippet is taken from the article Generic Sparse Array and Sparse Matrices in C#[^]
public int CompareTo(ComparableTuple8<TItem0, TItem1, TItem2, TItem3, TItem4, TItem5, TItem6, TItem7> group)
{
int result = this.Item0.CompareTo(group.Item0);
if (result == 0)
{
result = this.Item1.CompareTo(group.Item1);
if (result == 0)
{
result = this.Item2.CompareTo(group.Item2);
if (result == 0)
{
result = this.Item3.CompareTo(group.Item3);
if (result == 0)
{
result = this.Item4.CompareTo(group.Item4);
if (result == 0)
{
result = this.Item5.CompareTo(group.Item5);
if (result == 0)
{
result = this.Item6.CompareTo(group.Item6);
if (result == 0)
{
result = this.Item7.CompareTo(group.Item7);
}
}
}
}
}
}
}
return result;
}
#endregion
}
That is 8 keys. Now, imagine there were 16 keys. The nested form would require extreme indentation and it would be more difficult to read.
To break the code up into more comparison functions would be both inefficient and confusing. There could be a function that compared 16 keys that called a function that compared 15 keys, that called a function that compared 14 keys, etc., or some other breakdown such the the function that took 16 keys did four comparisons and called a function that did 12 key comparisons, that called a function that did 8 key comparisons, etc.. Those solutions are not good solutions.
An array could be used to to store the keys, and the comparison function could loop over they keys comparing them, however, that is less efficient, and in this case, look-up time is critical. Furthermore, not all keys necessarily derive from a common type, so the array could not be strongly typed. This would be particularly bad for a generic container.
For 16 keys, the loop form would be desirable, although of course the break statements would execute as the result of a comparison, not an error.
modified 10-Nov-13 13:52pm.
|
|
|
|
|
Examples like this really make the advantages of C++'s templates apparent.
That could all be done by recursion - evaluated at compile-time, and be generalised to any N-tuple.
It also makes the advantage of RAII apparent - use multiple returns safely with the guarantee of deterministic cleanup.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
This is C# code, (although I have a SparseArray template for C++ article on codeproject). This code does look just like C++ though.
The code was generated by a program. I had to create ComparableTuple generics for so many sizes, I wrote a program that generates the code. Still, I would want the code to be readable afterward.
If you meant the code generator could use recursion, I agree, although the code I wrote uses iteration. I expect that is almost certainly what you meant.
The run-time code shouldn't use recursion as that would be too inefficient, and this needs to be as fast as possible.
|
|
|
|
|
No, I was not referring to runtime recursion you'll be pleased to know. That would be horrendously inefficient - and in my experience when you're writing code like this efficiency is often an issue.
I am talking about code generation, but letting the compiler do the work for you using template metaprogramming. Modern C++ supports variadic templates, so a Tuple class can be declared something like:
template <typename T, typename... Args>
class Tuple : public Tuple<Args...>
{
};
That is a tuple of any number of arguments, each of any type.
Your CompareTo function can then be written using recursion that occurs entirely at compile-time. This does require a detailed knowledge of template meta-programming though, and is certainly beyond me explaining in a short response here.
Luckily, the standard library already includes such a class named std::tuple . Here's a short example of its use:
#include <tuple>
#include <iostream>
int main()
{
auto t1 = std::make_tuple(1, 2, 3, 4, 5);
auto t2 = std::make_tuple(1, 2, 3, 4, 6);
if (t1 < t2)
std::cout << "Less" << std::endl;
else if (t1 > t2)
std::cout << "Greater" << std::endl;
else
std::cout << "Equal" << std::endl;
return 0;
}
Given two tuples, this will actually generate at compile-time code more or less equivalent to the example you gave above.
Indeed, given the two tuples defined above, a good compiler will simply realise that all the values are known at compile time, using Visual C++ 2013, I checked this. The program I just gave generates assembly that is equivalent to writing:
int main()
{
std::cout << "Less" << std::endl;
}
Sadly, you need to be using a really modern compiler for this goodness - VC++ just added support in 2013 - but it least its now supported by the most recent versions of all the major compilers.
PS. If you really want to see the dirty details involved in this sort of metaprogramming let me know, it just may take me a bit longer to compose - I'm still finding my feet with this stuff myself.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
I have read "Modern C++ Design" by Andrei Alexandrescu, which covers template metaprogramming in great detail.
I use it, however, I don't use it when there is a simpler solution. In this case, the code is auto-generated by another program that writes the entire file. Also, the code I posted is written in C#, not C++.
modified 11-Nov-13 22:28pm.
|
|
|
|
|
I was aware this was C#, I was simply saying that this is an area where templates (as opposed to generics) pay dividends.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
Alan Kay.
|
|
|
|
|
I remember that when I started learning programming in 1985 or before that the version of the language I started with was build with no loop statements, except the FOR loop. This made it relied heavily on GOTO statement in the case of conditional loops. I think for languages,if any, with such structure one HAVE to use it, with care.
|
|
|
|
|
I have never needed to use a GOTO statement in any language since BASIC. And when I wrote a recursive extension to the GOSUB function (along with a couple other snazzy enhancements) in Commodore PET's BASIC, I never needed one again either.
Marc
|
|
|
|
|
I'll repeat an edit to my reply to Harold up above:
The problem with goto is not, and never really has been, with exiting a context abruptly, especially with RAII, but in entering another context at an arbitrarily point.
|
|
|
|
|
Because it's so easy to misuse and because other constructs make it much easier to write readable code.
Christian Graus
Driven to the arms of OSX by Vista.
Read my blog to find out how I've worked around bugs in Microsoft tools and frameworks.
|
|
|
|
|
I use it and feel good when it is in my code because so many reputable sources are saying "do not do it" but not really showing any good reason why it is bad.
|
|
|
|
|
It's often hard to understand the reasons if all you do is look at the code you've just written. You have to "look" at the code that it will grow into after a year or more of maintaining and extending that code however!
I've seen some huge 15-20 year old functions with several hundreds or even thousands of lines of code. After so many years, I could still guess the general shape and design of these functions when they were first written, and at that time, using goto was neither looked down upon quite as much, nor did it seem unreasonable in that particular piece of code. Over the years however, tons of features were added who required additional blocks of code; dozens of corner cases were detected that needed to be treated separately; and at least half a dozen developers added their differing visions of how the code should be formatted and designed.
One of the things I tried over the years is split up that code into smaller, better maintainable chunks, but I've found there is no easy way to ensure this won't break the multitude of corner cases handled by this code. I could move some of it into initialization functions, and extract a few of the special feature code blocks. But it was nigh impossible to disentangle the mass of conditional code and goto statements (few as there were) while ensuring that the code would behave exactly like it did before. With none of the original programmers around to help determine what exactly the code was supposed to do, breaking it was too high a risk to take.
I'm not saying that the goto statements were the sole reason for the sorry, unmaintainable state of the code, but they were the main reason why I was unable to transform it into something maintainable!
tl;dr:
If all you do is write short lived functions and programs then use goto at your hearts desire. But if that code that you're writing will go into a professional application that may live on and be maintained and exxtended for many years, then you better think ahead and don't introduce a legacy that is hard to bear and harder to kill!
|
|
|
|
|
Take a look at this decades old code - I bet you can easily read it: http://www.literateprogramming.com/adventure.pdf
|
|
|
|
|
My point wasn't about code that died decades ago, but code that continued to live and be modified over decades! Readability is just one aspect, maintainability is more important.
Besides, the author himself stated in the comments that his reason for using goto was implementing multistate transitions. Come on! I've used tools to generate those automatically from UML 10 years ago! And I could even choose if I wanted to generate the statemachine using swicth or inheritance! In other words, there are valid alternatives and even tools that help you generate the code.
GOTOs are a bit like wire coat hangers: they tend to breed in the darkness, such that where there once were few, eventually there are many, and the program's architecture collapses beneath them. (Fran Poretto)
|
|
|
|
|
OCaml is a live, thriving project with many new contributors. It's perfectly maintainable, and, in my books, an example of the perfect C coding style.
Linux has megabytes of commits every week. Far from being "dead for decades". So, what are you talking about?
What's the point of generating code which is perfectly readable anyway? State transition is, essentially, goto. Any higher level language will feature the semantically equivalent construction to represent state transitions. Yes, arrows in your UML are not any different from goto.
|
|
|
|
|
of ocaml is your idea of perfect coding style we have very different views how good codes looks or reads.
|
|
|
|
|
Ah, let me guess, your brain is mutilated by that pathetic OOP thingy?
Ok. Carry on writing your buggy, slow, unreadable code. But do not lecture the real programmers on how to do things the right way. You'll never be able to implement a bytecode interpreter faster than this one.
|
|
|
|
|
hrhrhr it gets better by the post
pathetic oop vs "real programmers" - mmd
|
|
|
|
|