rmartin@clear.com (Bob Martin) (11/20/90)
In article <IMP.90Nov16123956@marvin.Solbourne.COM> imp@marvin.Solbourne.COM (Warner Losh) writes: >In article <1990Nov10.191840.21113@clear.com> rmartin@clear.com (Bob Martin) writes: >: The lack of standard coding practice IS a big problem for software >: maintenance. > >Agreed. However, a bad coding standard can make life miserable for >everybody. > Yet _NO_ standard is still worse >: It specifies that functions should have single entrance and single >: exit points > >This is a bogus restriction, at least in terms of a single exit point. > Allow me to make a few additions to your piece of code... > if (allocate-memory) { > do some stuff > if (get-more-memory) { > do more stuff > if (open-file) { > do even more stuff > if (alloc more memory) { > ... > status = OK; --- free more memory > } > else { --- LogError(1); > status = NO_MEM --- } --- close file > } > else { --- LogError(2); > status = NO_FILE --- } --- free-more-memory > } > else { ___ LogError(3); > status = NO_MEM --- } ___ free allocated memory > } > else { ___ LogError(4); > status = NO_MEM --- } > > return status; > Here we see one of the real benefits of the technique. The free calls and close calls can be performed at the correct place, and it is trivial to insure that they are present and correct. The ErrorLogs pinpoint exactly which place in the code is failing. Your assertion that simple programs need not go through this is valid so far as it goes. But how many programs start simple and then grow... The burden is not very great, the complexity of the code is reduced, and the code is more robust against growth. -- +-Robert C. Martin-----+:RRR:::CCC:M:::::M:| Nobody is responsible for | | rmartin@clear.com |:R::R:C::::M:M:M:M:| my words but me. I want | | uunet!clrcom!rmartin |:RRR::C::::M::M::M:| all the credit, and all | +----------------------+:R::R::CCC:M:::::M:| the blame. So there. |
mat@mole-end.UUCP (Mark A Terribile) (12/09/90)
> >: The lack of standard coding practice IS a big problem for software > >: maintenance. The worst problem in maintenance (he said dogmatically) is that the maintenance programmers rarely ensure that the code `typography' is the same quality it should be in new code. Granted, often managers take lines of code with difference as some sort of a measure of badness of a fix. Unfortunately, tweaks instead of thorough changes in code can produce precocious schlerosis. . . . > >: It specifies that functions should have single entrance and single > >: exit points > > > >This is a bogus restriction, at least in terms of a single exit point. > > > Allow me to make a few additions to your piece of code... > > if (allocate-memory) { > > do some stuff > > if (get-more-memory) { etc ... Let me suggest that the worst problem here is not the addition of code, nor the need to ensure that resources are properly managed as scopes are entered and left, but that the program's typographical structure and decision organization have not been kept up with the extra functionality of the code. > Here we see one of the real benefits of the technique. The free > calls and close calls can be performed at the correct place, > and it is trivial to insure that they are present and correct. > The ErrorLogs pinpoint exactly which place in the code is failing. I disagree strongly with the assertions. First, the error returns/logging are widely seperated from the tests which recognize them; they are located WAY down below AND they interrupt the text of non-error-case code. I grant that the free and close calls are attached to scope. Unfortunately, that doesn't guarantee that someone who picks the code up will get the resource-management correct EVEN THOUGH it follows the simple principle of following the scope. (In C++, where you can program such that the resources are released automatically when the program goes out of scope, it's another matter ... but C++ also handles return s properly.) What we have is a `Lazy Man's Load.' Let me try it another way ... keeping code linear, so that conditions are not nested (and especially so that the small and rare case does not tag along on the end of the large and common case) AND keeping track of resource management explicitly. if( allocate-memory FAILS ) { LogError(4); return NO_MEM; } do some stuff if( get-more-memory FAILS ) { LogError(3); free allocated memory return NO_MEM } do more stuff if( open-file FAILS ) { LogError(2); free-more-memory free-memory return NO_FILE } do even more stuff if( alloc more-more-memory FAILS ) { LogError(1); close file free-more-memory free-memory return NO_MEM } do even more more stuff free more-more-memory close file free-more-memory free-memory return OK; The really serious STRUCTURAL problem is that you have a problem that requires an `interesting' sequence of attempted resources and do-stuff's. If a resource can be released early, it should be. We might have { declare file variable if( open-file FAILS ) { LogError(2); free-more-memory free-memory return NO_FILE } do even more stuff } In no case, in such code, should the `do-stuff's be more than one or two lines! Use function calls, whatever. Whether you use the linear approach (which I strongly endorse) or the manually-manage-resources-by-scope approach (which I don't endorse), you need to dedicate a whole function to expressing just the resource management. Use other functions to express the do-stuff's if they are non-trivial. Especially if the do-stuff's involve loops, put them in other functions so that the loop structure does not add its clutter to the resource management flow structure. It's really important that the exceptional, there's-a-problem-with-resources- not-algorithm cases be written right after the tests that detect them; don't carry them downward and let them interpose into the program text lines and lines below. -- (This man's opinions are his own.) From mole-end Mark Terribile