• Welcome to Valhalla Legends Archive.
 

TagBans

Started by CrAz3D, June 05, 2003, 10:15 PM

Previous topic - Next topic

smoke

Actually, there are some very practical uses for goto that make code much cleaner and less repetitive.

int foo(void)
{
   if( bar1() != SUCCESS ) goto FINISH;
   if( bar2() != SUCCESS ) goto FINISH;
   ...

   if( bar<n>() != SUCCESS ) goto FINISH;

   <clean up stuff>

   return SUCCESS;
FINISH:
   <clean up stuff here>
   return FAIL;
}


Now, there are some major drawbacks to using goto and labels.  The main one is variable instanciation.  For instance, in C++, the following just doesn't work since the compiler has no idea how to handle the stack pushs and pulls.


int foo(void)
{
   int bar1;

   goto FINISH;

   int bar2;
FINISH:

   int bar3;
}

Arta

#16
Quote from: Yoni on June 06, 2003, 08:02 PM
I'm left breathless.
I don't believe in Internet flaming, so I won't elaborate.

I, also, am left breathless.

Edit:

My god, just saw smoke's post. How can such idiocy have been advocated twice on the same post? Is this the final bullet in the head for the botdev forum?

tA-Kane

Quote from: herzog_zwei on June 06, 2003, 07:04 PMSure, you can buy the state of the art machine with 1TB of memory to run a program written with all the bloat.  Or you can get an old system and run it with 64MB if it were written with resource limits in mind.  Memory overhead for a string used in strstr: 1 byte.  Overhead for a basic_string class: depends on the compiler but you can bet it's well over 16 bytes.  That doesn't include the extra overhead in using the vtables.
Don't forget processor overhead. People with old processors (*cough*) are usually left unheard when it comes to how inefficient a program is, simply because "Hey, it runs fast on our brand new machine, so it must be completely fast and efficient, right?"

There's more programs than I can count which run much slower on my machine than they should if the programmer actually took the time to test for older machines' run times; and these are small programs which shouldn't be processor intensive (For one prime example, a fricking text editor runs like shit, and a brand new IDE with almost the same exact features as the version before it runs literally 20x slower than the version before it!)
Macintosh programmer and enthusiast.
Battle.net Bot Programming: http://www.bash.org/?240059
I can write programs. Can you right them?

http://www.clan-mac.com
http://www.eve-online.com

herzog_zwei

#18
Quote from: Moonshine on June 07, 2003, 01:56 AM
Hmm, you missed a few features: http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1047588051&id=1043284376

True, but that list is not complete either.  The point is C++ extends C to make it easier/safer to do certain things.

Quote from: c0ol on June 07, 2003, 02:04 AM
uhh goto in C and most other languages with control structures is usually bad form, instead use statements like break and continue.

If you're using goto when break/continue would suffice, you're abusing it.  I am in no way advocating gotos over structure.  What I am saying is there is a use for goto in good coding.  Just to give you an idea of how often I use it, I'd say it's in less than 0.01% of all of my code.  Don't use it often, but do realize that there is a time and place for it.  FYI, there are even people that advocate not ever using break/continue since they are just hidden gotos and break the flow.

Quote from: smoke on June 07, 2003, 02:15 AM
Now, there are some major drawbacks to using goto and labels.  The main one is variable instanciation.  For instance, in C++, the following just doesn't work since the compiler has no idea how to handle the stack pushs and pulls.


Yes, that is one of the drawbacks of C++'s ability to declare variables at use time instead of at the beginning of a scope.  However, the compiler should catch that problem and tell you to fix it (which usually just means move the declaration up further).

Quote from: Arta[vL] on June 07, 2003, 03:37 AM
My god, just saw smoke's post. How can such idiocy have been advocated twice on the same post? Is this the final bullet in the head for the botdev forum?

You might want to read this long reply by a famous person and ponder on why he doesn't think the same thing as you:


On Sun, 12 Jan 2003, Rob Wilkens wrote:
>
> However, I have always been taught, and have always believed that
> "goto"s are inherently evil.  They are the creators of spaghetti code

No, you've been brainwashed by CS people who thought that Niklaus Wirth
actually knew what he was talking about. He didn't. He doesn't have a
frigging clue.

> (you start reading through the code to understand it (months or years
> after its written), and suddenly you jump to somewhere totally
> unrelated, and then jump somewhere else backwards, and it all gets ugly
> quickly).  This makes later debugging of code total hell.  

Any if-statement is a goto. As are all structured loops.

Ans sometimes structure is good. When it's good, you should use it.

And sometimes structure is _bad_, and gets into the way, and using a
"goto" is just much clearer.

For example, it is quite common to have conditionals THAT DO NOT NEST.

In which case you have two possibilities

- use goto, and be happy, since it doesn't enforce nesting

  This makes the code _more_ readable, since the code just does what
  the algorithm says it should do.

- duplicate the code, and rewrite it in a nesting form so that you can
  use the structured jumps.

  This often makes the code much LESS readable, harder to maintain,
  and bigger.

The Pascal language is a prime example of the latter problem. Because it
doesn't have a "break" statement, loops in (traditional) Pascal end up
often looking like total shit, because you have to add totally arbitrary
logic to say "I'm done now".


Who might the author be?  Linus Torvalds, of course.  Scan through the Linux source code and you'll find a few gotos.  I haven't looked through all of it but if Linus did his job right, the gotos he allows aren't very abusive and lead to much cleaner/easier to read code (not to mention it'd run faster since there's less code to read in, execute, and cache).  I don't think anyone would agree with all of Linus' views, but few would doubt that he's sharp and tends to make good judgement calls... not to mention he's the maintainer of a very big project spanning continents and many different developers with different coding styles/philosophies.

http://www.elegant-software.com/software/punditry/linus.html

If that doesn't get you out of the goto brainwashing, O'Reilly's "Linux Device Drivers" book even mentions that it is okay to use:



Error recovery is sometimes best handled with the goto statement. We normally hate to use goto, but in our opinion this is one situation (well, the only situation) where it is useful. In the kernel, goto is often used as shown here to deal with errors.

...

This code attempts to register three (fictitious) facilities. The goto statement is used in case of failure to cause the unregistration of only the facilities that had been successfully registered before things went bad.

Another option, requiring no hairy goto statements, is keeping track of what has been successfully registered and calling cleanup_module in case of any error. The cleanup function will only unroll the steps that have been successfully accomplished. This alternative, however, requires more code and more CPU time, so in fast paths you'll still resort to goto as the best error-recovery tool.


http://www.xml.com/ldd/chapter/book/ch02.html#buierr

Arta

That's not what you said in your original post...

I will agree, tentatively, that it might sometimes be ok to use goto in a situation where conditionals don't nest, maybe, but even in that situation nesting is often advantageous in terms of clarity, since it's much more obvious what code will execute when - it's similar to the theory behind layered architectures such as OSI. That's not to mention the fact that gotos can also be nested, intentionally or otherwise, which is immeasurably worse than nesting IFs.

Anyway, this was your original post...

Quote

C:
{
 mutex.Lock();
 if(!a()) { mutex.Unlock(); return; }
 if(!b()) { mutex.Unlock(); return; }
 c();
 mutex.Unlock();
}

or better:
{
 mutex.Lock();
 if(!a()) goto done;
 if(!b()) goto done;
 c();
done:
 mutex.Unlock();
}



...which is just horrific and is is an example of exactly when NOT to use goto (Aside from never, obviously).

I'll overlook the fact that you're apparently using a class in C and assume that was a typo. Both of those code snippets are very badly coded and would be better written thus:

Quote
{
 mutex.Lock();
 if(a() && b()){
   c();
 }
 
 mutex.Unlock();  
 return;
}
Quote

In the huge, immeasurably vast number of cases, goto is simply an excuse for bad logic.

Camel

Quote from: herzog_zwei on June 06, 2003, 07:40 PM
Anyone saying you should never use goto is an idiot and probably writes buggy code that doesn't handle errors well (or quickly if they use exceptions).  Goto has its uses but it shouldn't be abused when it's not needed.  In the latter case, it's being used as a common exit point for the function (that handles resource cleanup) when errors occur (which sometimes isn't as simple as a single line of code).

+1 for using goto
the reason goto is so taboo is because it's possible to jump to a different function or position in code and crash because the registers aren't all set to what they should be (any half decent compiler will never allow this to happen, though)

herzog_zwei

#21
Quote from: Arta[vL] on June 07, 2003, 08:08 AM
I'll overlook the fact that you're apparently using a class in C and assume that was a typo. Both of those code snippets are very badly coded and would be better written thus:


I see what you're complaining about now.  That example was just some quick thing I came up with to show one advantage of using C++ to handle something like automatic unlocking versus C++ in a C manner.  The goto thing was just an afterthought.  Anyway, if it were in pure C, just replace mutex.Lock() with with lock(&mutex) and mutex.Unlock() with unlock(&mutex).

As an example for using goto, it is incomplete and bad.  I didn't want to spend time writing up lots of code but basically the intention was that the if(a()) is followed by other code and then the if(b()) followed by some other ones, thereby you won't be able to optimize it with a simple if(a() && b()).  If you want a better example, I'll take a more complete one from O'Reilly that looks closer to a real example.  It's specifically written in C.


int init_module(void)
{
int err;

 /* registration takes a pointer and a name */
 err = register_this(ptr1, "skull");
 if (err) goto fail_this;
 err = register_that(ptr2, "skull");
 if (err) goto fail_that;
 err = register_those(ptr3, "skull");
 if (err) goto fail_those;

 return 0; /* success */

 fail_those: unregister_that(ptr2, "skull");
 fail_that: unregister_this(ptr1, "skull");
 fail_this: return err; /* propagate the error */
}


You can't optimize that into a single if() any more and you can certainly do it w/o using goto, but how much more code are you willing to add and how much time are you willing to spend maintaining the code if you needed more complicated deinitializers?

Linus even thinks it's okay to split up a long if line into multiple ifs if it gets too long.  Instead of:

if(a && b && c && d)
 doSomething


or:

if(a)
 if(b)
   if(c)
     if(d)
       doSomething


(where the if's can't be optimized for some reason), he'd rather see:

if(!a || !b) goto next;
if(b && c)
 doSomething
next:


This is assuming that a, b, c, d are long names and not something cryptic like a 1 letter function/variable name and doSomething isn't just a 1 liner.  His reasoning is it's easier to comprehend if you don't need to scroll/wrap.  I've never done that even with long ifs in my code, but I have no problems with it.

If it were written in C++, the alternative to using goto in some cases would be to use exceptions.  If speed/size were an issue, you wouldn't want to use exceptions.  Exceptions are rarely (maybe even never) faster and incur lots of overhead (compared to checking error codes) per try.  Checking for errors with ifs (which you still need to do with exceptions, but you won't need as many) is still cheaper than setting up the stack for a try and unwinding it when there's an exception thrown.  I revisited it last year to see if it was feasible to use last year (after reading that they've figured out how to implement exceptions with negligible overhead) and it was still a noticeable difference  under both Linux and Windows.  I don't remember the exact details any more but it was something like: things they said you should do to incur less overhead (like declaring functions as nothrow if they don't throw exceptions) didn't seem to make any difference, while trying made some impact and throwing made a huge impact.

Like Linus, I don't really see what the big fuss is about when people that advocate the use of break/continue cry when other people use of goto.  break/continue are just goto statements with a different name, but with less room for mistakes since it uses implicit labels/goto addresses.  All conditionals are also gotos (try writing a program in Assembly without using any branches).  The deciding factor should be which to use to make it clear (to you as well as others).  People don't have time to audit every line of code written by someone else so they skim it.  If they see a break/continue, they know what to expect.  If they see goto deinit, they know what to expect.  If you repeatedly make code do a, b, c because you want "structure", they'll expect it to always say it'll do a, b, c and won't catch the bug where you made it do a, b or a, b, c, d because they'll just assume it's another one of those repetitive snippits.  The overall factor should be readability/maintainability, not complete adherence to an old paradigm that's good for most cases but not all (and thought up of during the times when, as Camel pointed out, compilers sucked).

Grok

Religion has no place in computer science.

Belief that goto is always bad and should never be used is based on what you were taught.

Especially those people in college (or high school) courses should beware they are repeating the gospel of their professors.  Do it for the final exams but, later, do try to deprogram.

indulgence

If Linus thought it was a good idea to go skydiving without a parachute and drink arsenic shooters, would you do it to?
<3

Arta

That is an example of good goto use, I agree, but situations like that don't occur often. Not for me, anyway.

Skywing

#25
Quote from: herzog_zwei on June 06, 2003, 07:40 PM
Quote from: Camel on June 06, 2003, 07:09 PM
a better c? the only thing that's useful at all is the ability of classes to have functions, and that can be used without violating *any* other of the definitions of c (not that there are any others).
as i said before, the only difference between c and c++ is overhead and classes. use classes, but dont use all that other iostream crap or whatever; it's a waste. printf is cooler than cout, anyways.

Agreed.  C++ is C with classes, polymorphism, (limited/broken) inheritance, and templates.  Aside from polymorphism and templates, the other things can be done in standard C (take a look at X-Windows) but can leave room for lots of mistakes (or you can consider that a "feature").  Most of the C++ libraries are crap and you should just use the C ones.  You should write C++ programs with a OO C mindset instead of an OO C++ one.  Even most built-in C++ RTTI implementations are bloated and not too useful; you should just write your own RTTI.  C++ does have better exception handling (you can do that in C as well but not quite as well) but it's not that great and should be used sparingly due to overhead incurred in its use (even when exceptions aren't thrown).  Templates are useful, but most of the time isn't needed (though a good application of it would be easy and almost effortless reference counting for objects).  Polymorphism is useful and what makes the compiler know what function to use.  Some forms of it can be done in OO C easily, but the one with the same name and varying arguments can't be done as easily (you can go out of your way and use variable args and extra logic to dispatch to the correct one, but why not just let the C++ compiler do it for your C code?).  One very useful thing about C++ over C is its automatic calling of the destructor when an object falls out of scope (not to mention you can declare variables in places other than the beginning of a scope); this ability lets you do things like lock a critical section of code by declaring a lock object on the stack, which will unlock when the destructor is called.  Example:


C++:
{
  Lock _lock(mutex);
  if(!a()) return;
  if(!b()) return;
  c();
}

C:
{
  mutex.Lock();
  if(!a()) { mutex.Unlock(); return; }
  if(!b()) { mutex.Unlock(); return; }
  c();
  mutex.Unlock();
}

or better:
{
 mutex.Lock();
 if(!a()) goto done;
 if(!b()) goto done;
 c();
done:
 mutex.Unlock();
}


Anyone saying you should never use goto is an idiot and probably writes buggy code that doesn't handle errors well (or quickly if they use exceptions).  Goto has its uses but it shouldn't be abused when it's not needed.  In the latter case, it's being used as a common exit point for the function (that handles resource cleanup) when errors occur (which sometimes isn't as simple as a single line of code).


I really think you're missing the point of C++.  It's a tool.  It's job is to make it easier for you to write programs.  Sure, you can write the same stuff in C, but so what?  I don't know about you, but I'll take C++ over C for the simple reason that it makes writing and maintaining things significantly easier.  Of course, there will be some lost efficiency for some things - but with a good compiler, the vast majority of C++ features will operate just as fast, if not faster, than your C versions of them, and without all the lost productivity (read: lost money, in a professional programming environment) writing and debugging them.

I'm not saying everything about C++ is great and all, but on the whole, it's a good thing.  My development software includes an efficient implementation of binary trees and linked lists, for instance - it saves me a great deal of time to be able to use these instead of writing my own (or copying and pasting code and making mistakes in the progress) for all of my programs.  Furthermore, I don't have to worry about debugging these implementations, because I know they will work out of the box.  No trying to make sure that there wasn't something I forgot to change in a copy-pasted linked list C "template" from another program, no long hours (read: lost money) spent tracking down subtle logic errors in sophisticated data structures such as associative arrays.  Instead, I get to focus on writing the program, as opposed to rewriting the same thing over and over.

Furthermore, most of the compiler-supplied libraries will be geared specifically towards the platforms for which the compiler and linker are designed to build programs for.  This means that, regardless of which platform you use, you can (or should) be able to expect good performance out of the standard libraries.  Unless you want to write and optimize your own libraries to do the same thing for every platform you're writing code for (again, lost money in a real environment), it's highly possible that you might be losing performance by relying on your own implementations.

Now, I know it isn't a perfect world, and I know there are lots of C++ standard library implementations which are less than ideal - however, I strongly believe that many of your comments about C++ are unfounded and, in general, untrue.  I mean, just because you can do something the hard way doesn't mean that you SHOULD do something the hard way.  There's nothing wrong with using standard libraries and language features to save valuable development and debugging time.  Being able to spend all the time you want on a program, optimizing away every single piece of it and reimplementing standard features with custom versions that run at the absolute maximum speed for your specific needs is an unrealistic luxury in the "Real World" programming environment.  The "Real World" places emphasis on things like: Code readability and maintainability - C++ certainly has many features which can be used to make code look much nicer and be a whole lot easier to follow, if used properly, short development cycles - getting your software out before the competition can earn you a lot of money, and reliability - mistakes can be very costly, especially if you end up having to rewrite software for free for customers because you screwed something up - C++ makes it easier to get it right the first time.

C and C++ both have their uses, but your bashing of it seems rather uninformed to the least.  Hopefully I'll be able to at least communicate some of why I think C++ is a good language through this discussion, but flat out uncompromising disagreement doesn't help anyone learn.

iago

Quote from: Moonshine on June 07, 2003, 01:56 AM
Quote from: herzog_zwei on June 06, 2003, 07:40 PM
Agreed.  C++ is C with classes, polymorphism, (limited/broken) inheritance, and templates.

Hmm, you missed a few features: http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1047588051&id=1043284376

Almost every one of those is directly based on the fact that classes were added (with the exception of inline)
This'll make an interesting test for broken AV:
QuoteX5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*


Moonshine

#27
Quote from: iago on June 08, 2003, 10:23 AM
Quote from: Moonshine on June 07, 2003, 01:56 AM
Quote from: herzog_zwei on June 06, 2003, 07:40 PM
Agreed.  C++ is C with classes, polymorphism, (limited/broken) inheritance, and templates.

Hmm, you missed a few features: http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1047588051&id=1043284376

Almost every one of those is directly based on the fact that classes were added (with the exception of inline)

With the exception of inline? What about:

References
Templates
Default arguments
Function overloading
Namespaces
Exception handling
Run-time type identification
// comments
True const
Declarations as statements
Automatically typedef'd struct tags
Type safe linkage
new and delete
bool keyword
Safer and more robust casting

Edit: Let's not forget the invaluable STL library.

herzog_zwei

#28
Skywing:
I never said you should never use C++.  I think it's a good language as well.  I generally choose to write in C++ with the extra bells/whistles over using C because of the things you pointed out.  The point of the things I've been discussing is you should choose the right thing for the job, not because some professor/book said you should do it one and only one way.  In order to do a good job of choosing the right thing (language/API/etc), you have to consider the strengths/weaknesses of that thing you're looking at, how much experience you have with it, how much time/resources you have, and most importantly, what would make it more manageable in the long run.  I generally choose C++ because it's a better C that makes it easier for me to program using the OO paradigm with.  I don't choose C++ because I think it's a good OO language (which it really sucks at compared to other OO languages like Java/Small Talk).  If you choose C++ for every single thing you do, you're not picking the right thing for the job at hand.  When you're starting out, that's fine because you want to learn.  Once you get past that stage, you should open your mind and look at things from an intellectual's POV and not blindlessly follow the teachings of professors/books.

Moonshine:
That list is not very good considering one of the more important things C++ has is left out: polymorphism.  It's what makes classes, same named functions, overloading, implicit casting, etc. work the way they do.  Many of the things on the list is related to the things I mentioned, but not all of them.

Skywing

#29
Quote from: herzog_zwei on June 08, 2003, 06:34 PM
Skywing:
I never said you should never use C++.  I generally choose to write in C++ with the extra bells/whistles over using C because of the things you pointed out.  The point of the things I've been discussing is you should choose the right thing for the job, not because some professor/book said you should do it one and only one way.  In order to do a good job of choosing the right thing (language/API/etc), you have to consider the strengths/weaknesses of that thing you're looking at, how much experience you have with it, and how much time/resources you have, and most importantly, what would make it more manageable in the long run.  I generally choose C++ because it's a better C that makes it easier for me to program using the OO paradigm with.  I don't choose C++ because I think it's a good OO language (which it really sucks at compared to other OO languages like Java/Small Talk).  If you choose C++ for every single thing you do, you're not picking the right thing for the job at hand.  When you're starting out, that's fine because you want to learn.  Once you get past that stage, you should open your mind and look at things from an intellectual's POV and not blindlessly follow the teachings of professors/books.

Moonshine:
That list is not very good considering one of the more important things C++ has is left out: polymorphism.  It's what makes classes, same named functions, overloading, implicit casting, etc. work the way they do.  Many of the things on the list is indirectly related to the things I mentioned, but not all of them.

I'm not blindly following teachers or books - as a matter of fact, I've had compatatively little "formal" education on the subject of C++; most of what I've learned I picked up on my own.  I can certainly say from my own experience that C++ is successful, though; here's from the output of a project-stats dumper I made for a long-term (multi-year) project of mine, done almost entirely in C++:
Quote372 source file(s), totalling 3534622 byte(s) and 116770 line(s) of code.
I'd say 3.5MB of code (yes, a large portion of it is object oriented, and it is efficient according to my benchmarks) is more success than blindly following something unrealistic, wouldn't you?

Just for the record, this project also incorporates C and even some x86 assembler code too - but the majority of it is C++.  I don't use C++ to the exclusion of other things; I've simply found that for the vast majority of what I do, it's the right tool for the job.  That program would be significantly larger and would have taken a great deal more time to do if it was written in mostly C, considering how much it uses standard library features that C simply doesn't provide.

|