Is goto Still Considered Harmful?

S

Stefan Ram

James Kuyper said:
Yes, but you've been asked to defend an assertion that there is a
C-specific preference for short functions.

My assertion is that in C we (the majority of educated
programmers of today) prefer short functions. I do not wish
to make any assertion about whether this holds or does not
hold for any other language.
 
K

Kenny McCormack

My assertion is that in C we (the majority of educated
programmers of today) prefer short functions. I do not wish
to make any assertion about whether this holds or does not
hold for any other language.

I'm sure that Jim & Kiki realized a few posts ago what you meant [*], but
it is their nature that they can't admit it now. They're committed to
seeing this through on the assumption that their (mis-) understanding of
what you wrote is the only possible interpretation.

It's a lot like the Catholic church. Their doctrine of infallibility means
that they can never, ever admit to having made a mistake [**].

[*] I.e., that you were simply making a statement of the form: Stop signs
are red. Note that this doesn't say anything about any other kind of signs
(other types of signs may or may not be red, but this doesn't in any way
impinge on the truth of the assertion that stop signs are red).

[**] In fairness, it must be noted that Kiki (and others like him) do on
occasion admit to being on technical matters (which in the context of this
newsgroup means specifically: interpretation of the C standards documents),
but I have never seen them admit to having misunderstood another posters
post (i.e., intentions). On these matters, Kiki, James, et al, are, like
the RCC, infallible.
 
K

Kaz Kylheku

My assertion is that in C we (the majority of educated
programmers of today) prefer short functions. I do not wish
to make any assertion about whether this holds or does not
hold for any other language.

I prefer functions not to be "unnecessarily long".

Sometimes "necessarily long" can mean a thousand lines or more.

If a large function is broken up into a small top function which calls many
smaller ones, but those smaller ones have no caller other than the top
function, then the change is pretty much a waste of effort.

Not only that, but C has poor support for dividing logic into functions.
Breaking up a large function into smaller ones can mean changing the structure
of the code in order to simulate the environment passing that naturally occurs
in a language with nested functions. What were originally nice, local
variables in the original function turn into some ugly structure or structures
that only exists in order to support the fragmentation of the logic into
multiple functions. These can harm the performance of the function, and also
trade back some of the maintainbility improvement arising from the breakup.

So, it is perfectly okay for some complicated, self-contained piece of logic
which offers no re-usable small pieces to any other part of the program to be
be represented as a big function that works with a bunch of local variables.

Breaking up logic into functions should only be done when:

- some of those functions will be called from more than one place, eliminating
repetitions.

- some of those functions are useful on their own and will end up being
called by other functions or modules.

- pieces of the original large code were being selectively dispatched by
a large switch or if/else ladder, so that when the function is broken up,
a dispatch based on function pointer indirection can streamline the flow
and possibly even be faster.

Experienced, mature programmers can handle large functions, and large functions
occur in all advanced, real-world projects.

For instance, the function _dl_map_object_from_fd in the glibc dynamic linker:

https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-load.c

goes from line 919 to 1623. (Commit 9455df...bef85a).

People always wish that the world conform to their own physical and
intellectual limitations. Grapes that are out of reach must be sour,
thought the Fox.

Maybe the real principle here is "small functions for small minds".
 
K

Ken Brody

Ken Brody said:
int some_function()
{
int retcode = 0;
...
if ( something_fails() )
{
retcode = -1;
goto done;
}
... more code [1] ...
if ( something_else_fails() )
{
retcode = -1;
goto done;
}
... more code [2] ...
if ( another_something_else_fails() )
{
retcode = -1;
goto done;
}
... etc etc etc ...
done:
clean_up();
return(retcode);
}

int f3(){ ... etc etc etc ... return 0; }
int f2(){ ... more code [2] ... return something_else_fails() ? -1 : f3(); }
int f1(){ ... more code [1] ... return something_else_fails() ? -1 : f2(); }
int f(){ int r = something_fails() ? -1; else f1(); clean_up(); return r; }

And this qualifies as "without going through hoops", how?

I can't begin to picture the complexity of f1(), etc., especially with
regards to local variables within some_function().
 
I

Ian Collins

Kaz said:
I prefer functions not to be "unnecessarily long".

Sometimes "necessarily long" can mean a thousand lines or more.

If a large function is broken up into a small top function which calls many
smaller ones, but those smaller ones have no caller other than the top
function, then the change is pretty much a waste of effort.

Not if you are unit testing the functions, which I guess counts as them
having more than one caller... I have never seen or written long
functions in code developed using TDD.
 
G

glen herrmannsfeldt

(snip, someone wrote)
You appear to be quoting http://www.c-faq.com/; it would have been good
to mention that. It's hard to tell just what you're quoting.
But there's nothing C-specific about the advice you cite; it just
happens to be from a site that deals with C. Would it not be equally
applicable to any other language?

Maybe not. An APL program could be way too long at one page.
Some say unreadable no matter how short.

For assembler, programs naturally tend to be longer.

But yes, for a wide variety of languages the optimal length is
probably not too different from C.

-- glen
 
G

glen herrmannsfeldt

I take the context of the newsgroup I am writing in into
account.
(snip)

. In classical FORTRAN, IIRC Functions can be defined, but
they are not »functions« in the sense of C (not recursive).

C does require functions to be recursive, but most aren't.
The mathematical meaning of the word FUNCTION doesn't require
it to be recursive.

But Fortran did in 1990 add recursion, though it must be declared
as an attribute to such functions.

There is an additional complication in Fortran, in that inside a
function the name of the function is normally a variable name, not
a function name. This can be changed in Fortran 90 and later to
allow for direct recursion.

-- glen
 
M

Malcolm McLean

Maybe the real principle here is "small functions for small minds".
Procedural decomposition is largely for the benefit of the programmer, not the
machine.
I'll happily write trivial little functions. They often make code clearer.
red = clamp(red, 0, 255)

is obvious, writing it out as two ternary expressions obscures what you are
doing.

If you've got difficulty breaking up large functions, the secret is often
to declare structures to hold related units of data. Functions then naturally
operate on those structures. They're not reusable, of course, but they do
make logical sense. For example if you're doing a flood fill, you can have
the border pixel queue as a struct QUEUE. All the memory management and
circular buffer code is abstracted, and you can see the basic logic.
Since structs can be declared in file scope, you're not polluting namespace.
 
L

Lowell Gilbert

Ken Brody said:
Ken Brody said:
int some_function()
{
int retcode = 0;
...
if ( something_fails() )
{
retcode = -1;
goto done;
}
... more code [1] ...
if ( something_else_fails() )
{
retcode = -1;
goto done;
}
... more code [2] ...
if ( another_something_else_fails() )
{
retcode = -1;
goto done;
}
... etc etc etc ...
done:
clean_up();
return(retcode);
}

int f3(){ ... etc etc etc ... return 0; }
int f2(){ ... more code [2] ... return something_else_fails() ? -1 : f3(); }
int f1(){ ... more code [1] ... return something_else_fails() ? -1 : f2(); }
int f(){ int r = something_fails() ? -1; else f1(); clean_up(); return r; }

And this qualifies as "without going through hoops", how?

I can't begin to picture the complexity of f1(), etc., especially with
regards to local variables within some_function().

I don't really follow what Stefan is trying to do, although I suspect
that it's more of a way to handle a decision tree than Ken's example of
cleaning up state. And Ken's code sample is missing the most compelling
argument for the "cleanup goto": often the clean_up() functionality
*can't* be moved into its own function, because it needs access to local
variables.
 
S

Stefan Ram

Lowell Gilbert said:
cleaning up state. And Ken's code sample is missing the most compelling
argument for the "cleanup goto": often the clean_up() functionality
*can't* be moved into its own function, because it needs access to local
variables.

I you would provide a specific example (without »...«), I
might then show how to refactor it to the style I prefer.
 
S

Stefan Ram

Supersedes: <[email protected]>

Lowell Gilbert said:
cleaning up state. And Ken's code sample is missing the most compelling
argument for the "cleanup goto": often the clean_up() functionality
*can't* be moved into its own function, because it needs access to local
variables.

If you or someone else would provide a specific example
(without »...«), I might then show how to refactor it to
the style I prefer.
 
J

John Bode

As others and I have said before: One might as well claim that in this
case, the problem is not the use of gotos, but the huge size of functions.

Large functions are not necessarily *hard* to debug, if you can be certain
of the control flow. I'm arguing that adding gotos to the mix makes a bad
situation worse.

The gotos were what made that particular code unfixable, not the size of
it; the logic was impenetrable because the code branched all
over the goddamned place with no apparent rhyme or reason. Even when
we did eventually figure it out, we could not fix anything without breaking
something elsewhere in the code; it was so tightly coupled with itself
that any change only degraded it.
This is comp.lang.c; so, when someone asks about »goto«, we assume he
refers to »goto in C«. In C, we (except beginners) write short functions.

Who's "we"?

I've written very large functions in the past, usually because I was
dealing with a brain-dead, simple-but-tedious-to-use interface where
the code was on the order of

huge_struct.a_member = a_value;
huge_struct.another_member = another_value;
huge_struct.yet_another_member = yet_another_value;
...
huge_struct.omg_how_many_of_these_things_are_there = gaaaaahhhhh;
do_something_with( huge_struct );

repeated multiple times with multiple huge_struct types; individual
functions could top out at 500-800 SLOC, 80% of those being simple
assignment statements because the API was *stupid*.
Of course, who here would deny that refactoring a huge old-style
monolithic program into a modern procedural and structured program is
a lot of (possibly unpleasant and difficult) work? But when one is doing
it for an appropriate pay, it might be fair.

For that particular program, we told the customer we could either rewrite
it from scratch or they could buy faster hardware to meet their performance
requirement.

They bought the faster hardware.
 
J

James Kuyper

The collective of C programmers who write short functions in C.

That's close to being a tautology: of course people who write short
functions tend to believe that functions should be short. Your comment
would have been more meaningful if you could have, correctly, used "we"
to refer to a larger, or at least more authoritative, group of people.
 
K

Kenny McCormack

That's close to being a tautology: of course people who write short
functions tend to believe that functions should be short. Your comment
would have been more meaningful if you could have, correctly, used "we"
to refer to a larger, or at least more authoritative, group of people.

Just can't admit that you f*cked up, can you?

(As I described in my earlier post...)
 
K

Kenny McCormack

Outlawing anything more than 16 lines or banning more than one return
statement are the action of small minded types with no bigger picture.[/QUOTE]

Otherwise known as "CLC regs".

Hence, this sub-thread topic is right up their alley.

--
But the Bush apologists hope that you won't remember all that. And they
also have a theory, which I've been hearing more and more - namely,
that President Obama, though not yet in office or even elected, caused the
2008 slump. You see, people were worried in advance about his future
policies, and that's what caused the economy to tank. Seriously.

(Paul Krugman - Addicted to Bush)
 
M

Malcolm McLean

It could be argued that in many cases its easier - especially with
multiple return statements..... When you read a book do you limit the
chapters to half a page?
If a paragraph goes over half a page I'd break it up. It does depend what
you're writing - fiction needs continuous flow, so the chapters are about
2,000 words, with these days maybe the odd blank line to break up sections.
Technical writing has far more short sections with headings.
Outlawing anything more than 16 lines or banning more than one return
statement are the action of small minded types with no bigger picture.
Early C reserved a keyword, I think it was called "entry" to indicate that
a function could be called from more than one point. It was never implemented,
and the consensus is that it was a bad idea.
Personally I use a variable called "answer" as the return for most functions,
and the function end "return answer". I try to have only one return, but
I'll occasionally return early on aborts (e.g. a statistical function called
with N = 0), and I won't fuss about a terminal if(conditon) return true else
return false. I won't write it as return (condition), which is harder to
read.
 
S

Stefan Ram

Malcolm McLean said:
I'll occasionally return early on aborts (e.g. a statistical function called
with N = 0),

When you write a function »double average( int const N ...«,
what value do you return on an abort?
 
G

glen herrmannsfeldt

(snip)
Early C reserved a keyword, I think it was called "entry" to
indicate that a function could be called from more than one point.
It was never implemented, and the consensus is that it was a bad
idea.

Fortran and PL/I have ENTRY, and sometimes it is convenient and
useful. For example, SIN and COS, written in assembly, are commonly
one routine that does argment reduction differently, but then goes
into the rest of the computation in common.

I was once debugging with gdb a gfortran program with ENTRY, and
was surprised at what it did. gfortran uses the same back end as
gcc, which it seems can't generate ENTRY. The result is one function
for each entry point, which then calls another with the appropriate
combination of arguments.

I believe it is depricated in Fortran, probably not in PL/I.

-- glen
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top