At work, I have a 450 line function. What it does is very simple:
1) validate the request; if it's invalid, send back an error.
2) look up some information.
3) send that information.
Step 1 is around 300 lines. But a majority of the lines are huge comment blocks describing what the check is for and referencing various RFCs. Yeah, I could have broken this part off into its own function, but ... there will only be one caller and doing so would be doing so for its own sake [1].
Step 2 is one line of code (it calls a function for the information).
Step 3 is the remainder; it's just constructing the response and sending it.
The code is straightforward. It's easy to follow. It's long because of the commentary.
[1] I did have to deal with a code base where the developer went way overboard with functions, most of which only have one caller. It's so bad that the main loop of the program is five calling levels deep. I once traced one particular code path down a further ten levels of function calls (through half a dozen files) before it did actual work.
When I confronted the developer about that, he just simply stated, "the compiler will inline all that anyway." And yes, the compiler we're using can do inlining among multiple object files, which makes it a real pain to debug when all you have is a core file.
> went way overboard with functions, most of which only have one caller. It's so bad that the main loop of the program is five calling levels deep
It almost sounds like you're talking about http://canonical.org/~kragen/sw/dev3/server.s! Or for that matter http://canonical.org/~kragen/sw/aspmisc/my-very-first-raytra...! But it's unlikely you've had to maintain either of them. I intentionally broke things up like that with the idea that it would make it easier to understand. In fact, I originally wrote httpdito as straight-line code and only later factored it into macros to make it easier to understand.
It sounds like you're saying that this was misguided and I should have just used nesting. I'll have to think about that for a while.
In reference to your 450 line function. What you just explained to me are three different sections to some algorithm that you have. One of the sections you already have as a separate function. However, the other two are quite large. Imagine you've just found this 450 line function while debugging. How would you know to not go through each line to find the problem, or step over all 300/1/150 line chunks of it?
There is benefit in being able to group the chunks, and maybe your comments are quite obvious in the grouping. But if I know I'm looking for a "validation" bug, and I come across a grouping of three functions which are called, say, "PrepareRFCRequest", "GetResponseInformation" and "ConstructResponse" then I can very easily deduce that they're not related to my problem and ignore them. You could say they're not my concern.
A lot of these things I talk about build upon each other. i.e. If you then go ahead and put some sort of input validation inside of your "GetResponseInformation" function, then the grouping / function abstraction is pretty much useless and may even be detrimental when debugging, e.g. in my example above.
"there will only be one caller and doing so would be doing so for its own sake"
No, there is benefit to putting it in its own function. Even if there is one caller. Because creating functions isn't always about reducing duplication, but of concerns/abstraction and to me above all composability.
"[1] I did have to deal with a code base where the developer went way overboard with functions, most of which only have one caller. It's so bad that the main loop of the program is five calling levels deep. I once traced one particular code path down a further ten levels of function calls (through half a dozen files) before it did actual work."
Perhaps you, I, and the developer you speak of have a different view of what the "main body/loop" of the program, or any program in general.
At work, I have a 450 line function. What it does is very simple:
1) validate the request; if it's invalid, send back an error. 2) look up some information. 3) send that information.
Step 1 is around 300 lines. But a majority of the lines are huge comment blocks describing what the check is for and referencing various RFCs. Yeah, I could have broken this part off into its own function, but ... there will only be one caller and doing so would be doing so for its own sake [1].
Step 2 is one line of code (it calls a function for the information).
Step 3 is the remainder; it's just constructing the response and sending it.
The code is straightforward. It's easy to follow. It's long because of the commentary.
[1] I did have to deal with a code base where the developer went way overboard with functions, most of which only have one caller. It's so bad that the main loop of the program is five calling levels deep. I once traced one particular code path down a further ten levels of function calls (through half a dozen files) before it did actual work.
When I confronted the developer about that, he just simply stated, "the compiler will inline all that anyway." And yes, the compiler we're using can do inlining among multiple object files, which makes it a real pain to debug when all you have is a core file.