[ Poll ] Infinite loops

A place to discuss the implementation and style of computer programs.

Moderators: phlip, Moderators General, Prelates

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Postby EvanED » Wed Apr 25, 2007 7:33 am UTC

bitwiseshiftleft wrote:
EvanED wrote:Pretty much any compiler that doesn't completely suck should support tail call optimization.

(In fact, it is very easy to make the above statement true by simply defining sucky compilers to include any that don't support tail recursion optimizations. ;-) Which wouldn't be a horribly unreasonable thing to do, at least for general purpose ones...)


True, but tail-calls in C/C++ are non-trivial. Consider the following stupid example, which uses the idiom of passing pointers to return a tuple:

...

If return_a_tuple actually just returns a tuple, then the tail call optimization here is safe. But what if it saves its arguments in a static variable, and looks at them in some later call? Then if GCC optimizes the tail call, you'll get references to the wrong stack frame.

This is a stupid example, but a lot of programs pass around pointers to stuff on the stack, and verifying that they don't get saved somewhere is a huge pain.

Languages with tail call optimizations generally just don't allow references to the stack. Some of them don't even use a call stack.


Those are good points.

Though Intel CC, GCC, and Visual C++ all regularly optimize away tail recursion in cases where they can establish that won't happen; for instance, all three optimize it away in evildave's example.

Also, probably any language that really gives you side effects, like ML, would have the same problem, just to a lesser extent because of the different conventions in which it's used.

Let's just say, I've worked with a LOT of 'sucky' C/C++ compilers.

(Most of them were made by M$.)


Ah yes, the people who gave us an autoptr that is backed by an implementation that has a non-explict void* constructor, allowing such constructs as "auto_ptr<int> p = new double;" that cause bizarre runtime errors that are next to impossible to track down.

(Actually, it looks like this isn't true... at least previous versions of VS have shipped with the Dinkumware libraries, so it actually ISN'T MS's fault.)

Though when was the last time you were working with it? VC++6 was not great, but the versions since have improved quite a bit.

User avatar
bitwiseshiftleft
Posts: 295
Joined: Tue Jan 09, 2007 9:07 am UTC
Location: Stanford
Contact:

Postby bitwiseshiftleft » Wed Apr 25, 2007 7:45 am UTC

EvanED wrote:Also, probably any language that really gives you side effects, like ML, would have the same problem, just to a lesser extent because of the different conventions in which it's used.


Not really. Tail-call elimination just replaces call; return with unwind; goto. This makes no difference unless the memory on the stack is going to be referenced again. If you don't allow references to the stack, or if you already have the analysis necessary to make sure they don't escape, then there's no way this could go wrong.

Last I checked, ML (SML/NJ, at least) doesn't use a call stack anyway. It just allocates everything on the heap.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Postby EvanED » Wed Apr 25, 2007 7:58 am UTC

bitwiseshiftleft wrote:
EvanED wrote:Also, probably any language that really gives you side effects, like ML, would have the same problem, just to a lesser extent because of the different conventions in which it's used.


Not really. Tail-call elimination just replaces call; return with unwind; goto. This makes no difference unless the memory on the stack is going to be referenced again. If you don't allow references to the stack, or if you already have the analysis necessary to make sure they don't escape, then there's no way this could go wrong.

Last I checked, ML (SML/NJ, at least) doesn't use a call stack anyway. It just allocates everything on the heap.


There's still a call stack even if it doesn't take the form it does in, say, C, right? I thought that it just allocated activation records on the heap instead of in the traditional stack area. And then tail recursion still helps because it would let you not keep allocating frames, but just reusing them.

But now that I think about it a little more, I don't think you can get references on the stack. I was thinking that it supports references (via 'ref' and !), but it's more like Java in that it goes out and allocates space for it. So I guess you don't have that problem after all.

I think it's sleep time for tonight.

User avatar
bitwiseshiftleft
Posts: 295
Joined: Tue Jan 09, 2007 9:07 am UTC
Location: Stanford
Contact:

Postby bitwiseshiftleft » Wed Apr 25, 2007 8:09 am UTC

EvanED wrote:There's still a call stack even if it doesn't take the form it does in, say, C, right? I thought that it just allocated activation records on the heap instead of in the traditional stack area. And then tail recursion still helps because it would let you not keep allocating frames, but just reusing them.


Yeah, you're right, there's a call stack on the heap. I don't remember exactly how it works. I don't think it reuses the frames; they're probably read-only to avoid nasty interactions with call-cc. But it probably only allocates new ones on non-tail calls.

EvanED wrote:I think it's sleep time for tonight.


ni-night :-)

User avatar
EndangeredMassa
Posts: 46
Joined: Wed Apr 25, 2007 7:16 pm UTC

Postby EndangeredMassa » Wed Apr 25, 2007 9:00 pm UTC

bavardage wrote:There's always the fun of Befunge

Code: Select all


>       >>>>>>>v       v<<<<<<<
      >>^      >>v   v<<      ^<<
     >^          >>vv<          ^<
     ^            v <            ^
     ^<          v<>>v          >^
      ^<<      v<<   >>v      >>^
        ^<<<<<<<       >>>>>>>^

That's awesome.

I'm a while(true) {} kinda guy myself.

evildave
Posts: 77
Joined: Wed Apr 25, 2007 3:52 am UTC

Postby evildave » Wed Apr 25, 2007 9:52 pm UTC

EvanED wrote:
bitwiseshiftleft wrote:Though when was the last time you were working with it? VC++6 was not great, but the versions since have improved quite a bit.


The 'latest' were well into the .NET versions, but the internal, fixed buffers that make the compiler crash are larger, but still present.

My nomination for a 'worst' compiler was for a bent little 'SunPlus' embedded video game CPU, that had a TON of bugs in it that I got to discover and report. They couldn't just hack a version of GCC like everybody else, nope. They had to reinvent a C/C++ compiler from god-only-knows what foundation and then make sure it could only be called from their horrible IDE, too.


... Oh yeah, to stay on topic, how about in a segmented memory architecture, making a function 64K long (or branch to the end of that block) and then 'wrap around' at the beginning? Yee-haw!

User avatar
smithman89
Posts: 13
Joined: Wed Apr 25, 2007 10:22 pm UTC

Postby smithman89 » Wed Apr 25, 2007 10:38 pm UTC

Well, for me, the ones that pop up unexpectedly are the best. But for implicit ones, i like incrementing for loops inside the body, especially in loops with confusing code, just to piss off my friends.

Code: Select all

public void whateverMethod()
{
     int x=0, y=1;
     for(int x; x<y; x++)
     {
          //complex, and utter exploitation of the "dot" notation
          y++;  //Obviously not that implicit though...
     }
}

I just hate it that, eventually, java will yell at you :(

User avatar
Patashu
Answerful Bignitude
Posts: 378
Joined: Mon Mar 12, 2007 8:54 am UTC
Contact:

Postby Patashu » Thu Apr 26, 2007 12:21 am UTC

tendays wrote:
Patashu wrote:Can someone explain to me what all the syntax in

Code: Select all

:(){ :|:&};:
means?


I'll rename : into boom and indent it a bit so that my explanation makes sense :

Code: Select all

boom(){
    boom | boom &
}
boom


So this defines a function called boom and calls it.
The behaviour of boom, "boom | boom &" means start boom twice, connect the output of the first to the input of the second, and return immediately. Because they are both sent to the background this means one call to boom will create two processes (each of which will call boom).
So basically you are asking your system to create as many processes as the os will allow, so, unless ulimit is set to a reasonable value, crash the system.


Makes perfect sense now, thanks.

User avatar
cmacis
Posts: 754
Joined: Wed Dec 13, 2006 5:22 pm UTC
Location: Leeds or Bradford, Thessex
Contact:

Postby cmacis » Thu Apr 26, 2007 12:34 am UTC

Ah, rambombs. The easiest breaky things to code.

That's a point in the safety debate, can non-windows oses fight back against rambombs? What about a rambomb that copies itself and runs the copies? It's not requiring supervisor access to do that is it?
li te'o te'a vei pai pi'i ka'o ve'o su'i pa du li no
Mathematician is a function mapping tea onto theorems. Sadly this function is irreversible.
QED is Latin for small empty box.
Ceci n’est pas une [s]pipe[/s] signature.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Postby EvanED » Thu Apr 26, 2007 12:45 am UTC

cmacis wrote:Ah, rambombs. The easiest breaky things to code.

That's a point in the safety debate, can non-windows oses fight back against rambombs? What about a rambomb that copies itself and runs the copies? It's not requiring supervisor access to do that is it?


Not usually, at least on your home computer and such, but you can place resource limits on user accounts. Not sure how flexible they are.

User avatar
evilbeanfiend
Posts: 2650
Joined: Tue Mar 13, 2007 7:05 am UTC
Location: the old world

Postby evilbeanfiend » Thu Apr 26, 2007 9:10 am UTC

evildave wrote:
EvanED wrote:
bitwiseshiftleft wrote:Though when was the last time you were working with it? VC++6 was not great, but the versions since have improved quite a bit.


The 'latest' were well into the .NET versions, but the internal, fixed buffers that make the compiler crash are larger, but still present.


is that strictly a compiler issue or a libstdc++ implementation issue? afaik m$ don't make make the libstc++ that they provide, dinkumware do. of course this is largely irrelevant to the end user.

vsc++ has been pretty good at conforming to the standard (apart from template export) since 7.0 which wasn't long after gcc got serious about supporting templates and namespaces properly.
in ur beanz makin u eveel

User avatar
tendays
Posts: 957
Joined: Sat Feb 17, 2007 6:21 pm UTC
Location: HCMC

Postby tendays » Thu Apr 26, 2007 1:22 pm UTC

cmacis wrote:Ah, rambombs. The easiest breaky things to code.

That's a point in the safety debate, can non-windows oses fight back against rambombs? What about a rambomb that copies itself and runs the copies? It's not requiring supervisor access to do that is it?


Set up a ulimit -u, people!

This is how I set up my ulimit (mostly defaults)

Code: Select all

$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
max nice                        (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) unlimited
max locked memory       (kbytes, -l) unlimited
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) unlimited
max rt priority                 (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 200
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

This is what I get when running that forkbomb on my system:

Code: Select all

$ :(){ :|:&};:
[2] 26614
$ bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
(...)
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable

[2]+  Done                    : | :
$

User avatar
hotaru
Posts: 1045
Joined: Fri Apr 13, 2007 6:54 pm UTC

Postby hotaru » Thu Apr 26, 2007 5:31 pm UTC

Code: Select all

#include <stdio.h>
#include <unistd.h>

int main(){
 for(;vfork(););
 fputs("DESU ",stdout);
 _exit(0);
}


except that my operating system tries to be too smart and stops it from forking after a while :(

evildave
Posts: 77
Joined: Wed Apr 25, 2007 3:52 am UTC

Postby evildave » Thu Apr 26, 2007 6:48 pm UTC

evilbeanfiend wrote:
evildave wrote:
EvanED wrote:
bitwiseshiftleft wrote:Though when was the last time you were working with it? VC++6 was not great, but the versions since have improved quite a bit.


The 'latest' were well into the .NET versions, but the internal, fixed buffers that make the compiler crash are larger, but still present.


is that strictly a compiler issue or a libstdc++ implementation issue? afaik m$ don't make make the libstc++ that they provide, dinkumware do. of course this is largely irrelevant to the end user.

vsc++ has been pretty good at conforming to the standard (apart from template export) since 7.0 which wasn't long after gcc got serious about supporting templates and namespaces properly.


It's strictly an internal compiler issue. IT CRASHES. It contains fixed internal buffers, so if you overflow them, you'll get an 'INTERNAL ERROR' and the compiler aborts. You have to turn off the 'precompiled headers' and 'incremental' everything and anything to get a (much slower) build, but that only postpones the 'die horribly' longer. The 'incremental' stuff breaks ANYTHING in the preprocessor that deals with a __LINE__ as a token, BTW.

Naturally the same code built in gnu without a problem, but I have my own subset of minor pet peeves with that. That episode where the pedantic dick-head decided that anything like 'offsetof' in C++ SHALL GENERATE A WARNING, with no way to disable said warning sort of ticked me off, because I do treat warnings as errors. The way he defended his inexcusable actions were sort of comical, too. I think it was 3.something when that happened. Had to 'work around' it by...

Code: Select all

#ifdef GNU
#define offsetof(c,m) ((size_t)((c*)(4))->m)-4)
#else
#define offsetof(c,m) ((size_t)((c*)(NULL))->m))
#endif


It made some simple code that 'created' native classes (assumed to be glorified structures in the object model I was using, not a virtual anything) from serial IO in order to communicate with some other runtime I had to deal with at the time a lot simpler to write and use.

What I ultimately did was, well, EVIL by most people's accounts, but I really, really liked it. It made my serial stuff absolutely version-proof and VERY fast.

It broke MSVC because I wrote things like....

Code: Select all

#define MyEnum( op ) \
   op( eFirst )\
   op( eSecond )\
   op( eThird )\

#define MyStruct( op ) \
    op( base, struct, MyOtherStruct, copy(template) )\
    op( x, scalar, int16, iminmax(0, 0,1000) )\
    op( y, scalar, int16, iminmax(0, 0,1000) )\
    op( name, string, char, "Bob" )\
    op( type, enum, MyEnum, eThird )\
    // Etc.


And then passed different 'op' macros to it that would make the class/struct, construct and destruct the class/struc, serialize the class/struct, XML read/write the class/struct, validate the class/struct, copy or clone the class/struct, etc.

The part that really choked Visual Studio.net was when I passed a set of these structs to another set of macros to declare and generate protocols and event dispatchers from a set of those definitions that would call your handler(s) with the incoming data already in a constructed class. That's what killed Visual Studio.net and blew its internal buffers, and needed to be 'engineered around'.

Code: Select all

// All structure definitions, like above
// Makes an enum, a message receiver, message sender, XML parser, XML writer, etc. from the data, based on what 'op' you drop in.
#define Protocol(op)
   op(Nak) \
   op(Ping) \
   op(Connect) \
   op(Password) \
   op(Disconnect) \
   op(Chat) \
   ... etc.


So, once you had these macros, expanding macros, expanding more macros, Visual Studio would shit its self.

Most people don't understand that when you have teams (not just ONE team, no, gotta split up everybody on the project into competing, dysfunctional cliques) working on a client/server project, it becomes critical to keep the protocol and data formats compatible. What happens is, somebody makes some small change, gets interrupted, and fails to make the 30 other changes that had to be made to support it.

So you get serial packets that are out of whack, data gets corrupted, and somebody spends hours and hours chasing the problem and eventually checking all 31 places that the change should have affected.

This kind of code gets rid of all of that. You don't 'step' into the serial code, it was all made for you by the compiler. At least, not after the core code generator's all written and verified. It makes the same operations on the data you'd have written by hand, very consistently.

Anyway, at the time, I was forced to work on a server that would build and run on either a Visual Studio/Windows environment, or a 'Unix/Linux' environment... so I endlessly tripped over all these little 'differences'. The client team wanted their own version of the server to 'debug' in Visual Studio. The server team, well, we didn't want the damned thing to crash, and to use more scalable hardware than Windows supported. Anyway, the edit/compile/test thing went sort of like this...

"All done! Let's see how it looks in Linux..."
"Crap! Let's fix that...
OK, let's see how it looks in Windows..."
"Crap!"

Lots of little spots where I had to do... ppMS(ms), ppGNU(gnu), ppMSGNU( ms,gnu ) things, as well as the more conventional #ifdef/#else/#endif things.

Ah, C++. Most people don't understand why I loathe it so. Not only do you deal with every difference in the target OS, you deal with every difference in compiler and library implementation, and it will NEVER get better. Use it at your own peril.

C code can be object oriented, you have TOTAL control over how it works, and at least the whole object and template model which you wrote it under won't crumble before your eyes as the compiler 'evolves' into ever more deluxe lumps of excrement.

Later versions of the 'object model' looked more like this, which worked around the internal issue, and made it at least 'step' in debuggers...

Code: Select all

//data.h
record_begin(XYZ)
   record_var(   z,   float32,   0.0f )
   record_var(   y,   float32,   0.0f )
   record_var(   x,   float32,   0.0f )
record_end(XYZ)
container_set(starmap,XYZ)
record_begin(Space)
   record_achar( name, char, 32, "unnamed" )
   record_obj( stars, starmap )
record_end(Space)


Then you'd define something and #include a factory that would define all the record_ things, include that defined string token to make a set of handlers for the data you defined, then undef and redefine the record_ things, until you had all the data declared and functions written.

Doodle77
Posts: 107
Joined: Mon Mar 26, 2007 9:46 pm UTC

Postby Doodle77 » Sun Apr 29, 2007 5:54 am UTC

Code: Select all

#define repeat(x) for (int i=0;i!=x;i++)

repeat(-1) {}

Seriously i use while(1) {}

The basic spells are launching standard user-level apps, and using them in the simplest way.
The first level of the arcane are menu items in programs.
Level two are keyboard shortcuts.
Level three are command line programs.
Level four is learning to hook command line programs together.
Level five is regular expressions and other concentrated ASPs.
Level six is batch files and other simple scripting/macro languages.
Level seven spells are basic computer programs, be it C Java or ASM.
Level eight spells are advanced algorithmics and techniques.
Level nine spells are entire operating systems, compilers and other powerful grimiors.

Woah, i actually did 1-7. Though i did 5 after 6 (i learned regular expressions while writing miniforums.)

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Postby You, sir, name? » Sun Apr 29, 2007 10:04 pm UTC

This is more entangled fishing line than spaghetti code. Ah, the joys of goto.

Code: Select all

#include <stdlib.h>

int main(void) {
  this: goto programming;
  is: goto to;
  bound: goto head;
  to: goto hair;
  make: goto bound;
  any: goto from;
  programming: goto rip;
  professor: goto this;
  rip: goto is;
  the: goto make;
  hair: goto any;
  from: goto the;
  his: goto professor;
  head: goto his;
  return EXIT_SUCCESS;
}

User avatar
wr3cktangle
Posts: 75
Joined: Tue Aug 01, 2006 5:03 pm UTC

Postby wr3cktangle » Sun Apr 29, 2007 11:40 pm UTC

i was always a for(;;){} person, but i think that's because for loops were my first loop type learned.

User avatar
Duck
Posts: 29
Joined: Wed Apr 11, 2007 1:53 pm UTC
Location: Somerset, UK
Contact:

Postby Duck » Mon Apr 30, 2007 3:34 pm UTC

What are all you people using these infinite loops for exactly? Ok, some may not consider me a real programmer - I mainly program games and php/mysql stuff - but I'm fairly certain I haven't encountered the need for an infinite loop in recent years.

For me there's always something more meaningful to go inside the while() clause relating to when the loop is required to be broken.

Or are you embedded systems programmers who write stuff which actually does needs to loop until the device runs out of power or is destroyed?

User avatar
EndangeredMassa
Posts: 46
Joined: Wed Apr 25, 2007 7:16 pm UTC

Postby EndangeredMassa » Mon Apr 30, 2007 3:56 pm UTC

I can't remember the last time that I had to use an infinite loop, but I can think of two cases where they would be useful.

1) The software needs to run forever. It sounds silly, but you could have a simple device that needs to operate until the user switches the power off, where you wouldn't need to check for power at the top of each loop.

2) There are several ways to exit the loop and it just doesn't make sense to wait until the condition is checked again before you exit.

I cannot come up with any specific examples right now, though.

User avatar
Dingbats
Posts: 921
Joined: Tue Mar 20, 2007 12:46 pm UTC
Location: Sweden
Contact:

Postby Dingbats » Mon Apr 30, 2007 4:48 pm UTC

Duck wrote:What are all you people using these infinite loops for exactly?

In Python it's often necessary to use an infinite loop because of the prohibitation against variable assignments as a termination condition. So where C would do:

Code: Select all

while(x = get_index(y)) {
...
}


Python would use:

Code: Select all

while True:
    x = get_index(y)
    if not x:
        break
    ...


And regardless of language you may have multiple end conditions and putting them all ANDed in the top would make it ugly to read, so you move them into the loop instead.

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Postby You, sir, name? » Mon Apr 30, 2007 9:57 pm UTC

Duck wrote:What are all you people using these infinite loops for exactly? Ok, some may not consider me a real programmer - I mainly program games and php/mysql stuff - but I'm fairly certain I haven't encountered the need for an infinite loop in recent years.

For me there's always something more meaningful to go inside the while() clause relating to when the loop is required to be broken.

Or are you embedded systems programmers who write stuff which actually does needs to loop until the device runs out of power or is destroyed?


The main program loop, for an instance?

If you're a game programmer, you should be familiar with this sort of construct:

Code: Select all

for(;;) {
  update_input();
  game_mechanics();
  paint_screen();
}


Where the loop would be broken inside update_input() when the player presses escape, or in game_mechanics when the player dies or some such.

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Postby EvanED » Mon Apr 30, 2007 10:12 pm UTC

You, sir, name? wrote:The main program loop, for an instance?

If you're a game programmer, you should be familiar with this sort of construct:

Code: Select all

for(;;) {
  update_input();
  game_mechanics();
  paint_screen();
}


Where the loop would be broken inside update_input() when the player presses escape, or in game_mechanics when the player dies or some such.


How do you break out of that loop from within that function?

I can think of three ways:
1. Call exit() (or terminate(), or...)
2. Throw an exception
3. setjmp/longjmp

None seem likely to me to actually show up, because none returns control to any cleanup code following the loop or anything like that.

I would suspect something like your stereotypical Windows message loop:

Code: Select all

while (GetMessage(&msg, NULL, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

which breaks when GetMessage returns 0.

Do people actually write loops like what you give and use exit() to leave the program while not returning from main directly?

Rysto
Posts: 1460
Joined: Wed Mar 21, 2007 4:07 am UTC

Postby Rysto » Tue May 01, 2007 12:50 am UTC

EvanED wrote:None seem likely to me to actually show up, because none returns control to any cleanup code following the loop or anything like that.

atexit()? set_terminate()?

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Postby EvanED » Tue May 01, 2007 1:00 am UTC

Rysto wrote:
EvanED wrote:None seem likely to me to actually show up, because none returns control to any cleanup code following the loop or anything like that.

atexit()? set_terminate()?


Okay, I'll bite... how do they return control to the function with the loop?

Yes, you *could* do cleanup with them (same with C++ destructors), but I have a suspicion that it's more common to just explicitly break out of the loop because it's easier to read that way. I could be wrong of course.

evildave
Posts: 77
Joined: Wed Apr 25, 2007 3:52 am UTC

Postby evildave » Tue May 01, 2007 3:40 am UTC

Hmm...

Code: Select all

/* Another endless loop */
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
   atexit((void (*)(void))main);
   putch( rand()&1?'/':'\\' );
}

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Postby You, sir, name? » Tue May 01, 2007 10:06 am UTC

EvanED wrote:How do you break out of that loop from within that function?

I can think of three ways:
1. Call exit() (or terminate(), or...)
2. Throw an exception
3. setjmp/longjmp

None seem likely to me to actually show up, because none returns control to any cleanup code following the loop or anything like that.


You either use atexit to automagically attach a clean up routine to the exit function, or simply make a wrapper function for exit that handles all the tidying up.

EvanED wrote:Yes, you *could* do cleanup with them (same with C++ destructors), but I have a suspicion that it's more common to just explicitly break out of the loop because it's easier to read that way. I could be wrong of course.


I personally never put cleanup code after the main routine. It's such a hazzle building an infrastructure for sending a message to the loop that the program needs to be terminated. Especially when there are several ways of reaching that state up the function hierarchy.

But I guess it's a matter of taste and tradition.

evildave
Posts: 77
Joined: Wed Apr 25, 2007 3:52 am UTC

Postby evildave » Wed May 02, 2007 5:20 pm UTC

Then there are always the single-threaded, cooperative multitasking, non-blocking infinite loops...

Code: Select all

#include <stdio.h>
#include <stdlib.h>

#define true 1
#define false 0

#define statecurr  -__LINE__
#define statebegin -1
#define stateend   ((int)(~0u>>1))

// Declare state machine properties as static vars
#define stateDeclStatic(label)\
    static int label = statebegin;\
    static int label##_loop = statebegin

// Declare state machine properties as members of some struct/class/etc.
#define stateDecl(label)\
    int label;\
    int label##_loop

// Initialize state machine variables
// If class members, 'label' will be 'class->label' when passed to macros
#define stateInit(label)\
   (label = label##_loop = statebegin)

// Declare initial state of state machine - REQUIRED
#define stateBegin(label) \
   switch(label)\
   {\
   case statebegin:

// A state to happen and yield control back to app
#define stateYield(label) \
      label = statecurr;\
      return true;\
   case statecurr:

// Set an anonymous loop in state
#define stateSetLoop(label) \
   label##_loop = statecurr;\
   stateYield(label)

// Wait for some condition
#define stateWait(label,booltest) \
   stateYield(label)\
      if( !(booltest) )\
         return true;

// Sleep forever until some event wakes us
#define stateSleep(label) \
   stateYield(label);\
      return true;\
   
// Set up an event handler
// Flow that reaches this will sleep
// event should be an enumeration, from 0 to whatever
#define stateEvent(label,event) \
   stateSleep(label)\
   case event:

// Declare last state and error handler
// Error handler can assert, throw, try to repair state, etc.
#define stateEnd(label,error) \
      label = stateend;\
      return true;\
   default:\
      {error}\
      return true;\
   case stateend:\
      (label = label##_loop = statebegin);\
      break;\
   }

// From within state machine, stop current state and repeat this state
#define stateRepeat(label)    return true

// From within state machine, stop current state and invoke loop for next cycle
#define stateLoop(label)    { label = label##_loop; return true; }

// From within state machine, stop current state and reset to first state
#define stateReset(label)    { label = label##_loop = statebegin; return true; }

// Anywhere within application, signal machine to jump to this event handler
#define stateEventSignal(label,event)    (label = event)
   

int main(void)
{
   // Continue while state machine runs
   while( StateMachine() )
   {
      // Do other real-time tasks
      //sleep(1);
   }
   return 0;
}

int StateMachine( void )
{
   stateDeclStatic(sm);
   stateBegin(sm)
   {
      printf( "Begin\n" );
   }
   stateSetLoop(sm)
   {
      printf( "Do someting\n" );
   }
   stateYield(sm)
   {
      printf( "Do someting else\n" );
   }
   stateWait(sm, (rand()%100 > 95) )
   {
      printf( "More stuff\n" );
      stateLoop(sm);
   }
   stateEnd(sm,printf("Invalid state/event!\n");return false;)
   {
      printf( "All done" );
   }
   return false;
}

EvanED
Posts: 4331
Joined: Mon Aug 07, 2006 6:28 am UTC
Location: Madison, WI
Contact:

Postby EvanED » Wed May 02, 2007 5:24 pm UTC

You, sir, name? wrote:
EvanED wrote:How do you break out of that loop from within that function?

I can think of three ways:
1. Call exit() (or terminate(), or...)
2. Throw an exception
3. setjmp/longjmp

None seem likely to me to actually show up, because none returns control to any cleanup code following the loop or anything like that.


You either use atexit to automagically attach a clean up routine to the exit function, or simply make a wrapper function for exit that handles all the tidying up.


This means that, without destructors, you can't clean anything that is stored in a local variable in main, which means you have to make them global (or at least file static).

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Postby You, sir, name? » Wed May 02, 2007 8:00 pm UTC

EvanED wrote:
You, sir, name? wrote:
EvanED wrote:How do you break out of that loop from within that function?

I can think of three ways:
1. Call exit() (or terminate(), or...)
2. Throw an exception
3. setjmp/longjmp

None seem likely to me to actually show up, because none returns control to any cleanup code following the loop or anything like that.


You either use atexit to automagically attach a clean up routine to the exit function, or simply make a wrapper function for exit that handles all the tidying up.


This means that, without destructors, you can't clean anything that is stored in a local variable in main, which means you have to make them global (or at least file static).


Yeh, I'm more of a C programmer than a C++-one.

evildave
Posts: 77
Joined: Wed Apr 25, 2007 3:52 am UTC

Postby evildave » Wed May 02, 2007 11:08 pm UTC

If you throw an exception (C++), the cleanup does happen ASSUMING your references/pointers have some sort of initialization-is-resource-allocation scheme to dereference/free whatever they happen to be holding.

If you 'exit', and have an atexit handler properly written, that's covered, too. After all, the only 'horrible' things in a game environment are going to be failing to stop the sound hardware or failing to restore the graphics mode. The rest gets handled by the OS. You can't 'leak' memory or handles on an OS level by aborting an app without freeing/closing things... unless the OS its self is horribly buggy. The 'best' place to put that clean-up actually is in some sort of termination handler, like atexit, or a global catch(...), because you WANT to be sure the clean-up happens no matter what. If the game's running in a Window and using system calls, then there's precious little chance you'll mess anything up by aborting, assuming you don't terminate in the middle of writing a file.

If you wrote it so setjmp/longjmp didn't bugger everything, that would work, too. In C++ code it would skip over destructors. This actually can be handled as well using scoping rules, and carefully thought out design, though you'd probably be much better off using an exception.

Code: Select all

// Nothing previous in the call stack can have any outstanding C++ variables.
void example(void)
{
    // No C++ variables
    bool condition = false;
    {
        // C++ variables in their own scope
    }
    if( condition )
        longjmp(someplace, 0);
    {
        // Other C++ variables in their own scope
    }
}

User avatar
Duck
Posts: 29
Joined: Wed Apr 11, 2007 1:53 pm UTC
Location: Somerset, UK
Contact:

Postby Duck » Thu May 03, 2007 9:37 am UTC

You, sir, name? wrote:If you're a game programmer, you should be familiar with this sort of construct:

Code: Select all

for(;;) {
  update_input();
  game_mechanics();
  paint_screen();
}


I would always use:

Code: Select all

while(!gameOver) {
  update_input();
  game_mechanics();
  paint_screen();
}


Where any number of events could flip the 'gameOver' flag.

User avatar
You, sir, name?
Posts: 6983
Joined: Sun Apr 22, 2007 10:07 am UTC
Location: Chako Paul City
Contact:

Postby You, sir, name? » Thu May 03, 2007 9:49 am UTC

Duck wrote:
You, sir, name? wrote:If you're a game programmer, you should be familiar with this sort of construct:

Code: Select all

for(;;) {
  update_input();
  game_mechanics();
  paint_screen();
}


I would always use:

Code: Select all

while(!gameOver) {
  update_input();
  game_mechanics();
  paint_screen();
}


Where any number of events could flip the 'gameOver' flag.


But then you have a global variable gameOver? It really isn't that far a stretch from having a global gameOver() function instead.

User avatar
CDarklock
Posts: 113
Joined: Thu May 03, 2007 8:13 am UTC
Location: Puyallup, WA
Contact:

Postby CDarklock » Thu May 03, 2007 9:49 am UTC

Duck wrote:I would always use:[...]Where any number of events could flip the 'gameOver' flag.


That's a memory access and comparison. That will impact your framerate.

Of course, today, that framerate hit isn't likely to amount to much... but if you ever built anything on an AT class machine, you remember when 30fps was the Holy Grail and anyone who could actually accomplish it was The Man.

I hate infinite loops, too, but I've got far too much time under my belt to puff out my chest and say they should never be used. Sometimes it matters.
And there was much rejoicing.

Hex
Posts: 67
Joined: Sat Apr 07, 2007 10:07 pm UTC

Postby Hex » Thu May 03, 2007 8:20 pm UTC

Wtf is with you people.

Code: Select all

  Loop:     
  ;
  jr Loop

User avatar
Hammer
Because all of you look like nails.
Posts: 5491
Joined: Thu May 03, 2007 7:32 pm UTC
Contact:

Re: [ Poll ] Infinite loops

Postby Hammer » Thu May 03, 2007 8:27 pm UTC

warriorness wrote:What is the best way to implement an infinite loop?


My personal favorite is to write a for loop, change it to a while loop and forget to add the line that increments the loop counter. *sigh*

BTW, I have a product which includes a flavor of Basic that allows users to program their forms. If I had a nickel for every time one of them lectured me about how the compiler should be able to catch that they'd written a while loop that never exits I could buy a small island. Of course, if I could write a compiler that could catch an endless while loop, I could also buy a small island.
"What's wrong with you mathematicians? Cake is never a problem."

User avatar
entropomorphic
Posts: 23
Joined: Mon Apr 02, 2007 2:51 pm UTC

Postby entropomorphic » Fri May 04, 2007 3:15 pm UTC

Gawd, I can't stand it when people don't use at least two of three parameters in a for-loop. JUST USE WHILE PEOPLE! IT'S THERE FOR A REASON AND IT USUALLY SCANS SYNTACTICALLY!

There, sorry. I'm done.

while(1) {}

while(1) {
fork();
}

It's not infinite, but my favorite loop is Duff's Device. This formation says something about C as a language... but I'm not sure what.

User avatar
torne
Posts: 98
Joined: Wed Nov 01, 2006 11:58 am UTC
Location: London, UK

Postby torne » Tue May 08, 2007 12:01 pm UTC

entropomorphic wrote:Gawd, I can't stand it when people don't use at least two of three parameters in a for-loop. JUST USE WHILE PEOPLE! IT'S THERE FOR A REASON AND IT USUALLY SCANS SYNTACTICALLY!

while(1) often makes the compiler issue a warning about a constant expression in a condition. for(;;) does not. That's all.
Last edited by torne on Wed May 09, 2007 10:49 am UTC, edited 1 time in total.

User avatar
Jach
Posts: 167
Joined: Sat May 05, 2007 8:38 pm UTC
Contact:

Postby Jach » Tue May 08, 2007 10:13 pm UTC

while 1:
*insert Python code here*

Go infinite loops!

User avatar
ZoFreX
Posts: 70
Joined: Thu May 10, 2007 11:23 pm UTC
Location: Bristol, UK

Postby ZoFreX » Fri May 11, 2007 12:07 am UTC

Rysto wrote:

Code: Select all

#define EVER ;;
...
for(EVER)

I didn't have a favourite method before, but I do now! :D

entropomorphic wrote:Gawd, I can't stand it when people don't use at least two of three parameters in a for-loop. JUST USE WHILE PEOPLE! IT'S THERE FOR A REASON AND IT USUALLY SCANS SYNTACTICALLY!

Semi-related pet peeve: I hate it when people use while loops such as

Code: Select all

current = root;
while(current != null)
{
  current = current->next;
}

Most people whose code I've seen seem to only use for loops for incrementing and comparing counters :(

Can someone please tell me how I should spell "whose" there, it's been annoying me for weeks

"Who's" means "who is". "Whose" means "of whom". e.g. "The code of whom I've seen". So you've spelt it right. - Not a mod

MissingDividends
Posts: 161
Joined: Fri May 25, 2007 8:59 pm UTC
Location: Cambridge, MA
Contact:

Postby MissingDividends » Fri May 25, 2007 10:50 pm UTC

entropomorphic wrote:while(1) {}

while(1) {
fork();
}


Why 1 as opposed to "true"? just shorter?
Personally, I think

Code: Select all

while(true){ }

is the most readable, but I've always been partial to:

Code: Select all

while(6*9!=42){ }


Return to “Coding”

Who is online

Users browsing this forum: No registered users and 8 guests