From: Grant Edwards on
On 2010-06-02, hamilton <hamilton(a)nothere.com> wrote:
> On 6/2/2010 9:12 AM, D Yuniskis wrote:
>> hamilton wrote:
>>>> Using assembly I would have used "goto" to steer execution where I
>>>> wanted
>>>> it, but how is it approached in C?
>>>
>>> There is no "goto" in C.
>>>
>>> "goto" in C is bad (very bad) practice.
>>
>> (sigh) No, goto has a role in C. What is wrong is using
>> goto as if it was GOTO (e.g., BASIC).
>>
>> Usually, you can restructure your code with suitable blocks
>> to eliminate the need for a "goto". But, there are times
>> when "goto" is the *right* choice. Convincing yourself
>> that it is "very bad practice" will blind you to when
>> it is the *right* practice!
>
> As you have stated "you can restructure your code".

There are times when a goto is simpley the best choice and results in
much simpler and eaiser to maintain code.

> Why change your code just to use a goto ??

No idea what you're asking.

> Design your code right the first time. ;-)

Sometimes a goto is the right way.

--
Grant Edwards grant.b.edwards Yow! Go on, EMOTE!
at I was RAISED on thought
gmail.com balloons!!
From: D Yuniskis on
Hi Royston,

Royston Vasey wrote:
> I've got a very basic question guys, I'm teaching myself C using Microchip's
> C18 compiler.

Great!

> I've got my main loop running, looking for certain conditions to occur,
> receiving a serial command & processing it.
>
> I need to monitor a few things for a time out condition, to do this I'm
> pre-loading Timer1 with the required values and when the timer overflows it
> jumps to the relevant interrupt.

What are you timing? I.e., are you trying to prevent the
code from waiting for a received character indefinitely?
Or, trying to abort a system call after a certain time limit
is exceeded? etc.

> All the above works fine.
>
> When the Timer1 interrupt fires what is the easiest/best/correct way to
> execute a block of "reset" code and then recommence execution at a
> _particular_ place in the main() loop?

<frown> You (typically) can't just jump around your code
"willy nilly" -- unless you really know what's going on under
the hood (doubtful if you're just learning).

E.g., when the IRQ is signalled, *something* was being done
at that time. That "something" has to be considered when
you decide to *abandon* it (by jumping to a *particular*
place in the main() loop).

If, for example, the code was busy *deep* in some nested
function call -- lots of cruft on the stack -- and you
just "jumped" to someplace else to "resume execution",
then you have all that cruft still sitting on the stack
with nothing to unroll it. (what happens if the *next*
interrupt does the exact same thing? and the one after
that? i.e., you eventually "blow the stack" because you've
never cleaned its previous contents off of it)

Even if you were lucky enough to be able to perform the
jump without consequence, you can't jump to any arbitrary
point in "main()" -- because you don't know what the
prerequisites for that part of main happen to be.
Consider, what would you expect the code to do if you jumped
into the middle of a "for" loop?

If you are building an application that relies on a simple
while loop for it's structure, then you have to poll every
"event" of interest in that loop and "dispatch" accordingly
*from* that loop. Your interrupt has to be "invisible"
to the application (in terms of how it *immediately*
affects program flow).

If you want to implement a simple timeout, try something like:
- set a (volatile) flag (*bit*!) someplace "common"
- have the ISR *clear* that flag when it occurs
- have whatever "service" you are trying to "time"
poll that flag along with whatever else it is supposed
to do; if it sees the flag get cleared "magically", the
service returns an error (TIMEOUT) instead of success

So, if you wanted to put a timeout on how long you would
wait for a character to be received:

result
receive_character(char *character, boolean *flag) {
while (*flag == SET) {
if (check_UART_for_character() == TRUE) {
*character = get_UART_character();
return SUCCESS;
}
return FAILURE;
}

I.e., as long as the flag is still *set*, keep checking the
UART to see if a character is available. If so, return the
character *and* indicate "SUCCESS".

However, once the flag is NOT "SET", stop checking the
UART and just return a FAILURE (TIMEOUT) indication.

Your main() then does something like:

// setup hardware
boolean waiting;

....
while (FOREVER) {
char character;
...
start_timer(&waiting, time_limit);
if (receive_character(&character, &waiting) == SUCCESS)
// do something with "character"
else
// character didn't show up in the prescribed time limit
...
}

void
start_timer(boolean *flag, timeout_t timeout) {
*flag = SET;
set_hardware_timer(timeout);
}

and your ISR does:

void
ISR(void) {
// diddle with the hardware
waiting = ~SET;
}

(I've not proofed this for typos :< )

> Using assembly I would have used "goto" to steer execution where I wanted
> it, but how is it approached in C?
From: D Yuniskis on
Hi Grant,

Grant Edwards wrote:
> On 2010-06-02, hamilton <hamilton(a)nothere.com> wrote:
>> On 6/2/2010 9:12 AM, D Yuniskis wrote:
>>> hamilton wrote:
>>>>> Using assembly I would have used "goto" to steer execution where I
>>>>> wanted
>>>>> it, but how is it approached in C?
>>>> There is no "goto" in C.
>>>>
>>>> "goto" in C is bad (very bad) practice.
>>> (sigh) No, goto has a role in C. What is wrong is using
>>> goto as if it was GOTO (e.g., BASIC).
>>>
>>> Usually, you can restructure your code with suitable blocks
>>> to eliminate the need for a "goto". But, there are times
>>> when "goto" is the *right* choice. Convincing yourself
>>> that it is "very bad practice" will blind you to when
>>> it is the *right* practice!
>> As you have stated "you can restructure your code".
>
> There are times when a goto is simpley the best choice and results in
> much simpler and eaiser to maintain code.
>
>> Why change your code just to use a goto ??
>
> No idea what you're asking.

Yes. Who said anything about *changing* it to *exploit*
a goto! Rather, *avoiding* the goto can be a colossal
waste of time and just make the code more brittle.

The goal of a HLL (if you want to call C such :> )
is to make it easier to *express* algorithms. Let
the compiler sort out how to get what you want
out of the code. (and the compiler will often
use *lots* of "goto's" -- jumps!)

>> Design your code right the first time. ;-)
>
> Sometimes a goto is the right way.

Exactly! -------------^^^^^^^^^^^^^^

I've seen horribly mangled code that went out of
its way to avoid using a goto. As a result, it's
a nightmare to maintain and very brittle.

Not all algorithms lend themselves to simple
blocks.
From: D Yuniskis on
Hi Grant,

Grant Edwards wrote:
> On 2010-06-02, D Yuniskis <not.going.to.be(a)seen.com> wrote:
>> Usually, you can restructure your code with suitable blocks to
>> eliminate the need for a "goto". But, there are times when "goto" is
>> the *right* choice. Convincing yourself that it is "very bad
>> practice" will blind you to when it is the *right* practice!
>
> I very much agree.
>
> There are a few places where a goto in a C program is, IMO, the best
> choice. Most notably when you need to "signal" an exception that must
> cause an exit from inside several nested loops or other control
> structures. Adding a bunch of flags to the loops to make them all exit
> prematurely due to the exception/failure is convoluted, hard to
> maintain, and will cause bugs later in the codes life.

Exactly. The "prima donna" syndrome may make you *feel* like
you've been extremely clever -- until your code breaks the
first time someone tries to modify it (this ignores the
costs to *you* to get it working correctly in the first place!)

> A simple "goto failure;" is much simpler and easier to read/maintain.
>
> If you look at C code in things like kernels and network stacks (code
> that was written by some very skilled people), you'll see the
> occasional goto used to handle what would be an exception in a
> language like Python.

In C, I often bracket "troublesome" regions in a superfluous
"do-while" and "break" from that in lieu of a "goto". But,
that assumes you don't *normally* exit that do-while "out the
bottom" (i.e., you either stay in it indefinitely *until*
one of those "breaks" occurs *or* you "return" out of it)

> I've also seen goto used to implement a retry mechanisms where adding
> another layer of looping would have been too convoluted.
From: Tim Wescott on
On 06/02/2010 07:35 AM, hamilton wrote:
> On 6/2/2010 6:24 AM, Royston Vasey wrote:
-- snip --

>>
>> Using assembly I would have used "goto" to steer execution where I wanted
>> it, but how is it approached in C?
>
> There is no "goto" in C.

There is a "goto" in C.

> "goto" in C is bad (very bad) practice.

"goto" in C can be about the worst thing that you can do to a program.

But used carefully and sparingly, "goto" in C can provide a very good
mechanism for exception handling. I use it occasionally, and it does
well for what it is. Were I to see a "goto" in a code review I would
pin the author of the code against the wall to make sure he was using it
correctly; were I to enter some of my code with a "goto" in a code
review I would schedule extra time to be pinned to the wall and justify
myself (I'd probably have careful comments in the code, too).

Exception handling in object-oriented programming is specifically
designed to address the problems in C++ that C's "goto" and long branch
(long goto? I can't remember what it's called) solve in C.

--
Tim Wescott
Control system and signal processing consulting
www.wescottdesign.com
First  |  Prev  |  Next  |  Last
Pages: 1 2 3 4 5 6 7 8
Prev: ready to run 32bit controller
Next: ARM7