Go Down

Topic: Effect of 'goto' on stack? (Read 2069 times) previous topic - next topic

kayson

In my code, if an error is encountered, an error function is called that holds the program in a loop until it receives one of two inputs. One continues where it left off, and the other is supposed to restart the program.

Pseudocode:
Code: [Select]

void setup()
{
//do setup stuff
}

void loop()
{
mainloop:

//loop stuff
}

void error(command)
{

while ( true )
{

if ( command = continue )
break;
else if ( command = restart )
{
setup();
goto mainloop;
}

}

}


Will using goto within a function cause problems? I'm thinking that if it compiles as a jump instruction won't it pop the stack. Unless the compiler takes care of it. All. If it is a problem, can I manually modify the return address so that on return the function jumps to the main loop?

Thanks!

AlphaBeta

I'm betting you're in for a ride :)

I thought the goto was scoped? I was actually quite sure of it.



I might as well be the one to say it: Do not use goto for this ;)

kayson

It is in fact scoped haha. I hadn't compiled in a while. Any suggestions on how to implement this? I need to exit the function and immediately return to the beginning of the loop.

Adrastos

#3
Jun 09, 2010, 11:01 pm Last Edit: Jun 09, 2010, 11:02 pm by Adrastos Reason: 1
Goto is bad. Murderous pink teddy bear bad. It is necromancy from assembly that has wormed its way into higher level programming languages.

There is *always* a better way of doing things in C.

Ex:
Code: [Select]

#include <avr/io.h>
#include <avr/wdt.h>

enum errors {
   eContinue,
   eRestart
};

boolean triedToUseGoto = false;

void setup()
{
   // make sure we have the watchdog disabled
   // (we might have enabled it to reset with)
   wdt_disable();
   
   // set me up!
   setupFunction();
}

void loop()
{
   // main code goes here
   triedToUseGoto = true;
   
   // check for an error
   if(triedToUseGoto)
   {
       // uh-oh! an error has arisen!
       error(eRestart);
   }
}

void error(int command)
{
   switch(command)
   {
       case eContinue:
           return;
           break;
           
       case eRestart:
           // we need to reset the entire micro
           // no re-running setup code here, cuz it
           // might stay persistant.
           // so what do we do?
           // enable the watchdog timer!!!
           // (and don't pat it)
           wdt_enable(WDTO_15MS);
           while(1);
           break;
   }
}


(Haven't tested, but something along those lines should work..)

Adrastos

Though I just realized that the above code won't work with the stock bootloader, as the watchdog would keep the chip in the bootloader forever.

You could either use Adafruit's bootloader which disables the watchdog in the bootloader, or you could simply wire a pin to control the reset line of the ATMega...

kayson

I ended up solving my problem with lots of return true/return falses...and a few goto's >.>

I know goto's aren't ideal, but theyre all to the same place (top of loop()) and I need them because there are so many nested loops :/

Imahilus

#6
Jun 10, 2010, 12:09 am Last Edit: Jun 10, 2010, 12:09 am by Imahilus Reason: 1
Nested loops are usually the result of poor design.
Would something allong the lines of a state machine work for you?
Essentially you'd be using the main loop for your nested loops...
Should help with debugging aswell, since you know the exact order in which your program runs, and can put in debug messages accordingly.

kayson

Solved all of my problems by using 'return'. Since loop() is just a function called within main(), using return exits loop() and hits the infinite for loop that Arduino uses, restarting loop().

crimony

Quote
There is *always* a better way of doing things in C.


There are some specific use-cases where the judicious application of goto results in faster, more readable (and hence more maintainable) and less error-prone code.

Device-driver code that sequentially allocates resources before performing actions, where failure to allocate a resource requires the de-allocation in the reverse order is one example. Using nested "if" statements in this case pushes the main code path deep into the nesting, obfuscating the code's function.

Goto has a place in the language, but it's definitely an advanced technique that should be considered only with strong justification.

westfw

Isn't this the sort of thing that "longjmp" and "setjmp" are used for?  I believe that they are carefully defined to do "reasonable" things with the stack.

The other possibility, this being a microcontroller, is to use a bit of in-line assembly language to jump to the VERY beginning of the sketch, where it sets up the stack and initializes memory.
Code: [Select]
 asm("jmp __ctors_end");
(an indirect jump via the "reset" vector would probably be better...)

halley

The goto keyword is seen as gauche in a higher-level language, but it's really not so bad as all that.  There are cases where it improves readability.  There's no technical difference between goto in C and a jump instruction in the assembler, except for the implications on other control structures like loops and functions.

AlphaBeta

The switch-case is essentially just a lot of gotos, many of us use that without thinking twice.

lloyddean

#12
Jun 12, 2010, 01:55 am Last Edit: Jun 12, 2010, 02:57 am by lloyddean Reason: 1
Not that I advocate what the original poster desires but I believe the following, untested and from memory, code should work as desired!

Code: [Select]

#include <setjmp.h>

jmp_buf g_there;

void error(command)
{
   while ( true )
   {
       if ( command == continue )
           break;
       else if ( command == restart )
       {
           longjmp(g_there, 1);
       }
   }
}

void setup()
{
}

void loop()
{
   int     jmp_return_value    = 0;

   // first time thru loop 'setjmp(g_there)' return 0
   
   if ( jmp_return_value = setjmp(g_there) )
   {
       // we've been called by 'longjmp', maybe do something special
       // depending upon value of 'jmp_return_value'
      setup();
       
       ...
   }
   
   // continue as normal
   ...
}


Go Up