|
|||
|
"mike3" <mike4ty4@yahoo.com> wrote in message
news:7e54cf3c-d514-407b-a9b6-c0009589d242@ra8g2000pbc.googlegroups.com... > ErrorCode MyFunc() > { > BigNum a, b, c, d; > ErrorCode rv; > > rv = BigNum_Initialize(&a); > if(rv != ERROR_SUCCESS) > return(rv); <etc> > But this is nasty, with all those duplicated "free" blocks. What can > be done to relieve this code duplication? It's ugly, it hurts my > freaking eyes and my nose is screaming for relief. And it hurts > maintainability to no end -- what if we decide to add more capability > to MyFunc() that requires additional bignums? Oy... We'd have to > update _all those blocks_. I think you've designed in too much error-checking, and made your bignums unwieldy to initialise and manage. Any code trying to actually do something with bignums would be lost amongst all the error-checking and recovery. And if there was some conditional code, and every single calculation needed checking, then you would easily lose track of what needed freeing and what didn't. Why does an initialisation need error-checking anyway? Does it involve allocating memory? It's not clear if a bignum is a pointer or a struct; they could be simply initialised to NULL or to {0}, leaving it to the bignum arithmetic routines to allocate as needed, and do the error-checking. Your code might still need to free, but the free routine can leave alone bignums that are still NULL or {0}. Actually, for memory management, you should take seriously the idea put forward of using garbage collection. Then you don't need to explicitly free memory. > ErrorCode MyFunc() > { > BigNum a, b, c, d; > ErrorCode rv; > > rv = BigNum_Initialize(&a); if(rv != ERROR_SUCCESS) goto > fail0; > rv = BigNum_Initialize(&b); if(rv != ERROR_SUCCESS) goto > fail1; > rv = BigNum_Initialize(&c); if(rv != ERROR_SUCCESS) goto > fail2; > rv = BigNum_Initialize(&d); if(rv != ERROR_SUCCESS) goto > fail3; (As a personal preference, I would use something like BN_Init(). Then these big names would dominate the code so much.) And, whatever the initialisation does, is it really necessary to check the result? Why not leave it to routines such as BigNum_Set() to validate it's arguments; if an argument has not been properly initialised, then *it* returns a failure code. > > /* do some math */ > rv = BigNum_Set(&b, 3); if(rv != ERROR_SUCCESS) goto fail4; > > ... > > /* free buffers */ > rv = BigNum_Free(&d); if(rv != ERROR_SUCCESS) goto fail3; And what is likely to go wrong with BigNum_Free() that will need checking? Will the caller of this function care, provided it gets the right answer? Or is the result of BigNum_Set() likely to be invalidated if a subsequent free fails? > rv = BigNum_Free(&c); if(rv != ERROR_SUCCESS) goto fail2; > rv = BigNum_Free(&b); if(rv != ERROR_SUCCESS) goto fail1; > rv = BigNum_Free(&a); if(rv != ERROR_SUCCESS) goto fail0; > > return(ERROR_SUCCESS); > > fail4: BigNum_Free(&d); > fail3: BigNum_Free(&c); > fail2: BigNum_Free(&b); > fail1: BigNum_Free(&a); > fail0: return(rv); > } If you do need to do this, then just combine all the statuses of multiple calls to BigNum_Free() (the error code needs to be of a format that will allow | or & operations). I know your post is about managing error recovery, rather than the merits of a particular library. But, someone needing to do arithmetic via function calls, might prefer to keep error-checking low-key, perhaps something like this: ErrorCode BigNum_Average(BigNum A, BigNum B, BigNum Result) { BigNum Two=NULL; ErrorCode e=0; e |= BigNum_Add(A,B, Result); e |= BigNum_Set(&Two, 2); e |= BigNum_Div(Result, Two, Result); e |= BigNum_Free(&Two); return e; } Then it is still reasonably easy to follow what's going on. (Actually, I wouldn't bother with the error codes myself; I would build a status into the bignum itself, a bit like the Nans of floating point.) -- Bartc |
|
|
||||
|
||||
|
|
|
|||
|
ram@zedat.fu-berlin.de (Stefan Ram) writes:
>ram@zedat.fu-berlin.de (Stefan Ram) writes: >>mike3 <mike4ty4@yahoo.com> writes: >>>be done to relieve this code duplication? It's ugly, it hurts my >>The code below still has some duplication. I might write another >>post later with less duplication. >/* This general function can be put in a library */ The next variant is less clean/general due to object identifiers with file scope, but the »application code« now is as concise as possible. /* This general function can be put away into a library */ ErrorCode BigNum_New ( ErrorCode( *continuation )( void ), BigNum * const n ) { ErrorCode rv = BigNum_Initialize( &n ); if( rv == ERROR_SUCCESS ) { rv = continuation(); BigNum_Free( &n ); } return rv; } /* here the application code starts */ static BigNum a; static BigNum b; static BigNum c; static BigNum d; static ErrorCode MyFuncX( void ){ return BigNum_Set( &b, 3 ); } static ErrorCode MyFuncD( void ){ return BigNum_New( MyFuncX, &d ); } static ErrorCode MyFuncC( void ){ return BigNum_New( MyFuncD, &c ); } static ErrorCode MyFuncB( void ){ return BigNum_New( MyFuncC, &b ); } ErrorCode MyFunc( void ){ return BigNum_New( MyFuncB, &a ); } |
|
|||
|
ram@zedat.fu-berlin.de (Stefan Ram) writes:
>( ErrorCode( *continuation )( void ), BigNum * const n ) >{ ErrorCode rv = BigNum_Initialize( &n ); > if( rv == ERROR_SUCCESS ) > { rv = continuation(); BigNum_Free( &n ); } Oops, I think »&n« should become »n« above. |
|
|||
|
ram@zedat.fu-berlin.de (Stefan Ram) writes:
>ram@zedat.fu-berlin.de (Stefan Ram) writes: >>ram@zedat.fu-berlin.de (Stefan Ram) writes: >>>mike3 <mike4ty4@yahoo.com> writes: >>>>be done to relieve this code duplication? It's ugly, it hurts my >>>The code below still has some duplication. I might write another >>>post later with less duplication. >>/* This general function can be put in a library */ >The next variant is less clean/general due to object >identifiers with file scope, but the »application code« >now is as concise as possible. In this special case, it can be made a little more concise: /* This general function can be put away into a library */ /* (unchanged since the last post, except for an error correction) */ ErrorCode BigNum_New ( ErrorCode( *continuation )( void ), BigNum * const n ) { ErrorCode rv = BigNum_Initialize( n ); if( rv == ERROR_SUCCESS ) { rv = continuation(); BigNum_Free( n ); } return rv; } /* here the application code starts */ static BigNum a[ 4 ]; static int i; static ErrorCode MyFuncX() { return i < 4 ? BigNum_New( MyFuncX, a + i++ ): BigNum_Set( a + 1, 3 ); } ErrorCode MyFunc( void ){ i = 0; return MyFuncX(); } |
|
|||
|
"mike3" <mike4ty4@yahoo.com> ha scritto nel messaggio news:7e54cf3c-d514-407b-a9b6-c0009589d242@ra8g2000pbc.googlegroups.com... > Hi. > > I've got some stuff that looks like this: this is my way... ErrorCode MyFunc(void) {BigNum a, b, c, d; ErrorCode rv, rk; if((rv=BigNum_Initialize(&a))!= ERROR_SUCCESS) return rv; if((rv = BigNum_Initialize(&b)) != ERROR_SUCCESS) {ex1: BigNum_Free(&a); return rv;} if((rv = BigNum_Initialize(&c)) != ERROR_SUCCESS) {ex2: BigNum_Free(&b); goto ex1;} if((rv = BigNum_Initialize(&d)) != ERROR_SUCCESS) {ex3: BigNum_Free(&c); goto ex2;} if((rv = BigNum_Set(&b, 3)) != ERROR_SUCCESS) {ex4: BigNum_Free(&d); goto ex3;} ... /* free buffers */ rv=ERROR_SUCCESS; if((rk=BigNum_Free(&d))!=ERROR_SUCCESS) rv=rk; if((rk=BigNum_Free(&c))!=ERROR_SUCCESS) rv=rk; if((rk=BigNum_Free(&b))!=ERROR_SUCCESS) rv=rk; if((rk=BigNum_Free(&a))!=ERROR_SUCCESS) rv=rk; return rv; } > But this is nasty, with all those duplicated "free" blocks. What can > be done to relieve this code duplication? It's ugly, it hurts my > freaking eyes and my nose is screaming for relief. And it hurts > maintainability to no end -- what if we decide to add more capability > to MyFunc() that requires additional bignums? Oy... We'd have to > update _all those blocks_. > > I was thinking about using a "goto", but Gotos are Bad, aren't they? I > can't believe I may have to use a goto! What about Djikstra's famous > letter? Is it possible that perhaps a goto may be GOOD here? Because > with a goto this would all seem much simpler. If we violate Djikstra, > we get: > > --- > > ErrorCode MyFunc() > { > BigNum a, b, c, d; > ErrorCode rv; > > rv = BigNum_Initialize(&a); if(rv != ERROR_SUCCESS) goto > fail0; > rv = BigNum_Initialize(&b); if(rv != ERROR_SUCCESS) goto > fail1; > rv = BigNum_Initialize(&c); if(rv != ERROR_SUCCESS) goto > fail2; > rv = BigNum_Initialize(&d); if(rv != ERROR_SUCCESS) goto > fail3; > > /* do some math */ > rv = BigNum_Set(&b, 3); if(rv != ERROR_SUCCESS) goto fail4; > > ... > > /* free buffers */ > rv = BigNum_Free(&d); if(rv != ERROR_SUCCESS) goto fail3; if above fail you not free c, b and a... > rv = BigNum_Free(&c); if(rv != ERROR_SUCCESS) goto fail2; > rv = BigNum_Free(&b); if(rv != ERROR_SUCCESS) goto fail1; > rv = BigNum_Free(&a); if(rv != ERROR_SUCCESS) goto fail0; > > return(ERROR_SUCCESS); > > fail4: BigNum_Free(&d); > fail3: BigNum_Free(&c); > fail2: BigNum_Free(&b); > fail1: BigNum_Free(&a); > fail0: return(rv); > } |
|
|||
|
On 2012-05-30, mike3 <mike4ty4@yahoo.com> wrote:
> I was thinking about using a "goto", but Gotos are Bad, aren't they? I > can't believe I may have to use a goto! What about Djikstra's famous > letter? Is it possible that perhaps a goto may be GOOD here? Because > with a goto this would all seem much simpler. If we violate Djikstra, > we get: The name is Dijkstra, not Djikstra. |
|
|||
|
On May 30, 1:44*am, mike3 <mike4...@yahoo.com> wrote:
> Hi. > > I've got some stuff that looks like this: > > --- > ErrorCode MyFunc() > { > * * * * * BigNum a, b, c, d; > * * * * * ErrorCode rv; > > * * * * * rv = BigNum_Initialize(&a); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * * return(rv); > > * * * * * rv = BigNum_Initialize(&b); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&a); /* and what if THIS fails??? */> * * * * * * return(rv); > * * * * * } > > * * * * * rv = BigNum_Initialize(&c); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&b); > * * * * * * BigNum_Free(&a); > * * * * * * return(rv); > * * * * * } > > * * * * * rv = BigNum_Initialize(&d); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&c); > * * * * * * BigNum_Free(&b); > * * * * * * BigNum_Free(&a); > * * * * * * return(rv); > * * * * * } > > * * * * * /* do some math */ > * * * * * rv = BigNum_Set(&b, 3); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&d); > * * * * * * BigNum_Free(&c); > * * * * * * BigNum_Free(&b); > * * * * * * BigNum_Free(&a); > * * * * * * return(rv); > * * * * * } > > * * * * * ... > > * * * * * /* free buffers */ > * * * * * rv = BigNum_Free(&d); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&c); > * * * * * * BigNum_Free(&b); > * * * * * * BigNum_Free(&a); > * * * * * * return(rv); > * * * * * } > > * * * * * rv = BigNum_Free(&c); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&b); > * * * * * * BigNum_Free(&a); > * * * * * * return(rv); > * * * * * } > > * * * * * rv = BigNum_Free(&b); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * { > * * * * * * BigNum_Free(&a); > * * * * * * return(rv); > * * * * * } > > * * * * * rv = BigNum_Free(&a); > * * * * * if(rv != ERROR_SUCCESS) > * * * * * * return(rv); > > * * * * * return(ERROR_SUCCESS);} > > --- ErrorCode MyFunc() { BigNum a, b, c, d; ErrorCode rv; rv = BigNum_Initialize(&a); rv = rv == ERROR_SUCCESS ? BigNum_Initialize(&b) : rv; rv = rv == ERROR_SUCCESS ? BigNum_Initialize(&c) : rv; rv = rv == ERROR_SUCCESS ? BigNum_Initialize(&d) : rv; if (rv != ERROR_SUCCESS) { BigNum_Free(&d); BigNum_Free(&c); BigNum_Free(&b); BigNum_Free(&a); } return(rv); } \endcode The main issue with this style is that it assumes that BigNum_Free operates like 'free' where a NULL pointer is a no-op. If you can initialize your BigNum variables to a corresponding "NULL" BigNum state, and BigNum_Free interprets a "NULL" BigNum as a no-op, you can duplicate this style. You may need to add a distinct struct initializer to your variable declarations if BigNum is not a pointer. \code /* Define an initialize macro to create a "NULL" BigNum */ #define BIGNUM_NULL_INIT { 0, NULL, ... } BigNum a = BIGNUM_NULL_INIT; BigNum b = BIGNUM_NULL_INIT; BigNum c = BIGNUM_NULL_INIT; BigNum d = BIGNUM_NULL_INIT; \endcode On a side note, the term ERROR_SUCCESS appears contradictory to me. I would prefer something like NO_ERROR or SUCCESS if you had control over the error definitions. Best regards, John D. |
|
|||
|
On Wednesday, May 30, 2012 9:55:55 AM UTC+1, MarkBluemel wrote:
> On 05/30/2012 06:44 AM, mike3 wrote: > > > But we've violated Djikstra and we've used a "bad" goto. How would > > _you_ write MyFunc() above? > > I personally don't like the infinitely indented if approach... nor me. Even goto is cleaner. |
|
|||
|
mike3 <mike4ty4@yahoo.com> writes:
> I've got some stuff that looks like this: <snip code> Just because no one has yet used an array: ErrorCode MyFunc(void) { enum { a, b, c, d, n_big_nums }; BigNum num[n_big_nums]; ErrorCode rv; int n = 0; while (n < n_big_nums && BigNum_Initialize(&num[n]) == ERROR_SUCCESS) n++; if (n == n_big_nums) do { /* do some math */ rv = BigNum_Set(&num[b], 3); if (rv != ERROR_SUCCESS) break; /* ... */ } while (0); while (n--) BigNum_Free(&num[n]); return rv; } You don't have to use the do { ... } while (0); thing. Just add a label to the end loop and goto it instead of using break. You have to switch a, b and so on to num[a], num[b] but it does extend easily if you find you need another BigNum. (Note that "ErrorCode MyFunc(void)" is a prototype whereas "ErrorCode MyFunc()" is not.) <snip> -- Ben. |
|
|||
|
tom st denis wrote:
) On May 30, 5:18?am, Willem <wil...@toad.stack.nl> wrote: )> If you want to be more concise, you can make ERROR_SUCCESS equal 0, )> and then you can simply do: )> )> ? ? rv = BigNum_Initialize(&a) || ... || BigNum_Set(&b, 3) || BigNum_AddTo(&a, &b) || ... ; )> )> Which will short-circuit at the first failure. ) ) Which in a sufficiently complicated function like an ECC point add/ ) double would result in spaghetti code and your termination. :-) How can it be spaghetti when it's a single line of calls, one after the other? Spaghetti code is meant to be tangled, isn't it? ) The "if (err == OK) ..." method is cool but frankly I just like the ) goto's when I'm writing out something the long way. I've seen the (err == OK) method used too much in code that didn't require cleanup at end of function. Apparently somebody at our company believes in 'one-function-one-return' religiously. I agree gotos are better. Another method is to have all 'bignum' functions check for 'error' inputs and generate 'error' outputs when they encounter them (or whenever they encounter an error), and then you only have to check for an 'error' result at the very end. Something like: int DoCalc() { int rv; BigNum a; BigNum b; BigNum c; BigNum d; BigNum_Init(&a); BigNum_Init(&b); BigNum_Init(&c); BigNum_Init(&d); BigNum_Set(&a, 3); BigNum_Set(&b, 5); ... BigNum_Multiply(&c, &d); ... if ((rv = BigNum_GetError(&d)) != ERROR_SUCCESS) return rv; BigNum_Print("The result is: %bn", &d); BigNum_Free(&a); BigNum_Free(&b); BigNum_Free(&c); BigNum_Free(&d); return ERROR_SUCCESS; } The only weirdside to this is when a value is not used in the final result, then that value will not be checked for errors. SaSW, Willem -- Disclaimer: I am in no way responsible for any of the statements made in the above text. For all I know I might be drugged or something.. No I'm not paranoid. You all think I'm paranoid, don't you ! #EOT |
|
|||
|
mike3 wrote:
[...] > But we've violated Djikstra and we've used a "bad" goto. How would > _you_ write MyFunc() above? Hello, If you want to get rid of the gotos and the ifs, you might end up using some kind of object stack. Every time you create or initialize something, you push a handle and a destructor onto the stack. If something goes wrong, you work your way down the stack destroying all the resources. A bit like pthread_cleanup_push/pop. Of course it is kludgy, may involve unsafe casts and a bit of additional overhead. I do not use it much myself. There is an implementation here: http://members.aon.at/~aklamme4/dvbv..._1.0.23.tar.gz In the files common/custck.c include/custck.h (please ignore the code smell) JK |
|
|||
|
Ike Naar <ike@iceland.freeshell.org> wrote:
> On 2012-05-30, mike3 <mike4ty4@yahoo.com> wrote: > > I was thinking about using a "goto", but Gotos are Bad, aren't they? I > > can't believe I may have to use a goto! What about Djikstra's famous > > letter? Is it possible that perhaps a goto may be GOOD here? Because > > with a goto this would all seem much simpler. If we violate Djikstra, > > we get: > > The name is Dijkstra, not Djikstra. The name is Dumbsonofabitch, not Dijkstra. And he's dead. Good riddance! |
|
|||
|
On May 30, 6:28*am, "BartC" <b...@freeuk.com> wrote:
> "mike3" <mike4...@yahoo.com> wrote in message <snip> > I think you've designed in too much error-checking, and made your bignums > unwieldy to initialise and manage. > > Any code trying to actually do something with bignums would be lost amongst > all the error-checking and recovery. And if there was some conditional code, > and every single calculation needed checking, then you would easily lose > track of what needed freeing and what didn't. > > Why does an initialisation need error-checking anyway? Does it involve > allocating memory? > Yes. You have to allocate memory to initialize (for the buffer holding the bignum's digits.). And that can fail. Thus, I return an error code. > It's not clear if a bignum is a pointer or a struct; they could be simply > initialised to NULL or to {0}, leaving it to the bignum arithmetic routines > to allocate as needed, and do the error-checking. Your code might still need > to free, but the free routine can leave alone bignums that are still NULLor > {0}. > > Actually, for memory management, you should take seriously the idea put > forward of using garbage collection. Then you don't need to explicitly free > memory. > > > ErrorCode MyFunc() > > { > > * * * * *BigNum a, b, c, d; > > * * * * *ErrorCode rv; > > > * * * * *rv = BigNum_Initialize(&a); if(rv != ERROR_SUCCESS) goto > > fail0; > > * * * * *rv = BigNum_Initialize(&b); if(rv != ERROR_SUCCESS) goto > > fail1; > > * * * * *rv = BigNum_Initialize(&c); if(rv != ERROR_SUCCESS) goto > > fail2; > > * * * * *rv = BigNum_Initialize(&d); if(rv != ERROR_SUCCESS) goto > > fail3; > > (As a personal preference, I would use something like BN_Init(). Then these > big names would dominate the code so much.) > > And, whatever the initialisation does, is it really necessary to check the > result? Why not leave it to routines such as BigNum_Set() to validate it's > arguments; if an argument has not been properly initialised, then *it* > returns a failure code. > What do you do with that failure code? See, you still need ahandler around every math routine call. This would help with the Init() functions' handlers, though. > > > > * * * * */* do some math */ > > * * * * *rv = BigNum_Set(&b, 3); if(rv != ERROR_SUCCESS) goto fail4; > > > * * * * *... > > > * * * * */* free buffers */ > > * * * * *rv = BigNum_Free(&d); if(rv != ERROR_SUCCESS) goto fail3; > > And what is likely to go wrong with BigNum_Free() that will need checking? > Will the caller of this function care, provided it gets the right answer?Or > is the result of BigNum_Set() likely to be invalidated if a subsequent free > fails? > Looking at the free function, it can return one error code: ERROR_INVALID_STORAGE_TYPE. A bignum can be stored on either memory or on disk (disk not yet implemented, but the stuff is there to allow for its implementation). There is, of course, a field in the bignum that indicates which is being used. If this is something weird -- something other than "memory" or "disk", then the "Free" function would get confused as it wouldn't know how to properly free the bignum. Normally, that shouldn't happen. But if someone was doing something silly, the catch is there to catch that. So, should I just remove that error return and say, "well, if you're messing around with this in some improper fashion, then expect things to break"? But then we run into the problem of where the field is corrupted due to a memory bug. In this case, a fail could be useful (although one of the other bignum routines could puke as well.). An idea I had is to drop the type field altogether and use something like this: typedef struct BigNum { .... Digit *RAMBuffer; FILE *DiskBuffer; .... } BigNum; and when BigNum is initialized to RAM storage, RAMBuffer holds a ptr, and DiskBuffer is NULL, and when BigNum is initialized to Disk storage, RAMBuffer is NULL, and DiskBuffer holds a ptr. Then there's no field that can get an invalid value, and if someone tries to set the NULL ptrs to something else, then expect things to break. (If a ptr gets corrupted, you're pretty screwed anyway and there isn't much you can do about it) > > * * * * *rv = BigNum_Free(&c); if(rv != ERROR_SUCCESS) goto fail2; > > * * * * *rv = BigNum_Free(&b); if(rv != ERROR_SUCCESS) goto fail1; > > * * * * *rv = BigNum_Free(&a); if(rv != ERROR_SUCCESS) goto fail0; > > > * * * * *return(ERROR_SUCCESS); > > > fail4: * *BigNum_Free(&d); > > fail3: * *BigNum_Free(&c); > > fail2: * *BigNum_Free(&b); > > fail1: * *BigNum_Free(&a); > > fail0: * *return(rv); > > } > > If you do need to do this, then just combine all the statuses of multiple > calls to BigNum_Free() (the error code needs to be of a format that will > allow | or & operations). > The thing here is, that I have a general "universal" list of error codes, numbered 0 (= success), 1, 2, 3, etc. and functions take their return codes from that. Perhaps that's not the best way to do it? (I was inspired by Microsoft's Windows system with all its error codes.) E.g. we could use for the bignum routines a special set of 32-bit (or even 64-bit, in this case -- this program is for 64-bit systems anyway) error codes with at most 32 possible errors (probably way more than'd be needed from a simple bignum routine!) error codes that indicate errors by bit flags (all zeroes = success), and could be ORed together to get what errors occurred in the code under consideration. > I know your post is about managing error recovery, rather than the meritsof > a particular library. But, someone needing to do arithmetic via function > calls, might prefer to keep error-checking low-key, perhaps something like > this: > > ErrorCode BigNum_Average(BigNum A, BigNum B, BigNum Result) { > BigNum Two=NULL; > ErrorCode e=0; > > *e |= BigNum_Add(A,B, Result); > *e |= BigNum_Set(&Two, 2); > *e |= BigNum_Div(Result, Two, Result); > > *e |= BigNum_Free(&Two); > > *return e; > > } > > Then it is still reasonably easy to follow what's going on. (Actually, I > wouldn't bother with the error codes myself; I would build a status into the > bignum itself, a bit like the Nans of floating point.) > Yes, a "NAN" or error flag is another possibility. Flag would be best -- it's real easy to check and you could set multiple values -- like 1 for bad alloc, 2 for overflow, etc. |
|
|||
|
On May 30, 2:19*pm, "io_x" <a...@b.c.invalid> wrote:
> this is my way... Why does this not surprise me? > ErrorCode MyFunc(void) > {BigNum a, b, c, d; > *ErrorCode *rv, rk; > > *if((rv=BigNum_Initialize(&a))!= ERROR_SUCCESS) > * * * * * * * * * * * * * * *return *rv; > *if((rv = BigNum_Initialize(&b)) != ERROR_SUCCESS) > * * {ex1: * BigNum_Free(&a); return *rv;} > *if((rv = BigNum_Initialize(&c)) != ERROR_SUCCESS) > * * {ex2: * BigNum_Free(&b); goto * ex1;} > *if((rv = BigNum_Initialize(&d)) != ERROR_SUCCESS) > * * {ex3: * BigNum_Free(&c); goto * ex2;} > *if((rv = BigNum_Set(&b, 3)) * * != ERROR_SUCCESS) > * * {ex4: * BigNum_Free(&d); goto * ex3;} Ouch! |
|
|||
|
mike3 <mike4ty4@yahoo.com> writes:
> I've got some stuff that looks like this: > > --- > ErrorCode MyFunc() > { > BigNum a, b, c, d; > ErrorCode rv; > > rv = BigNum_Initialize(&a); > if(rv != ERROR_SUCCESS) > return(rv); > > rv = BigNum_Initialize(&b); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&a); /* and what if THIS fails??? */> return(rv); > } > > rv = BigNum_Initialize(&c); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&b); > BigNum_Free(&a); > return(rv); > } > > rv = BigNum_Initialize(&d); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&c); > BigNum_Free(&b); > BigNum_Free(&a); > return(rv); > } > > /* do some math */ > rv = BigNum_Set(&b, 3); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&d); > BigNum_Free(&c); > BigNum_Free(&b); > BigNum_Free(&a); > return(rv); > } > > ... > > /* free buffers */ > rv = BigNum_Free(&d); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&c); > BigNum_Free(&b); > BigNum_Free(&a); > return(rv); > } > > rv = BigNum_Free(&c); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&b); > BigNum_Free(&a); > return(rv); > } > > rv = BigNum_Free(&b); > if(rv != ERROR_SUCCESS) > { > BigNum_Free(&a); > return(rv); > } > > rv = BigNum_Free(&a); > if(rv != ERROR_SUCCESS) > return(rv); > > return(ERROR_SUCCESS); > } > --- > > But this is nasty, with all those duplicated "free" blocks. What can > be done to relieve this code duplication? It's ugly, it hurts my > freaking eyes and my nose is screaming for relief. And it hurts > maintainability to no end -- what if we decide to add more capability > to MyFunc() that requires additional bignums? Oy... We'd have to > update _all those blocks_. > > I was thinking about using a "goto", but Gotos are Bad, aren't they? I > can't believe I may have to use a goto! What about Djikstra's famous > letter? Is it possible that perhaps a goto may be GOOD here? Because > with a goto this would all seem much simpler. If we violate Djikstra, > we get: > > --- > > ErrorCode MyFunc() > { > BigNum a, b, c, d; > ErrorCode rv; > > rv = BigNum_Initialize(&a); if(rv != ERROR_SUCCESS) goto > fail0; > rv = BigNum_Initialize(&b); if(rv != ERROR_SUCCESS) goto > fail1; > rv = BigNum_Initialize(&c); if(rv != ERROR_SUCCESS) goto > fail2; > rv = BigNum_Initialize(&d); if(rv != ERROR_SUCCESS) goto > fail3; > > /* do some math */ > rv = BigNum_Set(&b, 3); if(rv != ERROR_SUCCESS) goto fail4; > > ... > > /* free buffers */ > rv = BigNum_Free(&d); if(rv != ERROR_SUCCESS) goto fail3; > rv = BigNum_Free(&c); if(rv != ERROR_SUCCESS) goto fail2; > rv = BigNum_Free(&b); if(rv != ERROR_SUCCESS) goto fail1; > rv = BigNum_Free(&a); if(rv != ERROR_SUCCESS) goto fail0; > > return(ERROR_SUCCESS); > > fail4: BigNum_Free(&d); > fail3: BigNum_Free(&c); > fail2: BigNum_Free(&b); > fail1: BigNum_Free(&a); > fail0: return(rv); > } > --- > > But we've violated Djikstra and we've used a "bad" goto. How would > _you_ write MyFunc() above? Your question is really about style. As is usually the case in such matters, there are no right or wrong answers, just individual preference, and perhaps local culture that should also be taken into account. That said, when a question like this comes up, usually it's a strong indication that the program design is wrong at some higher level. If you find yourself writing lots of ugly functions, there's a good chance it will be worth rethinking some higher-level decisions and see if a different approach leads to cleaner code at the function level. |
|
|
![]() |
| Thread Tools | |
| Display Modes | |
|
|