Go Back   Rhinocerus > Newsgroup > Newsgroup comp.lang.c

Reply
 
Thread Tools Display Modes
  #16 (permalink)  
Old 05-30-2012, 12:28 PM
BartC
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

"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

Reply With Quote
Alt Today
Advertising
 
and become member of Rhinocerus
Standard Sponsored Links

  #17 (permalink)  
Old 05-30-2012, 12:39 PM
Stefan Ram
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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 ); }

Reply With Quote
  #18 (permalink)  
Old 05-30-2012, 12:42 PM
Stefan Ram
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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.

Reply With Quote
  #19 (permalink)  
Old 05-30-2012, 01:05 PM
Stefan Ram
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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(); }

Reply With Quote
  #20 (permalink)  
Old 05-30-2012, 01:19 PM
io_x
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.


"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);
> }




Reply With Quote
  #21 (permalink)  
Old 05-30-2012, 01:56 PM
Ike Naar
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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.
Reply With Quote
  #22 (permalink)  
Old 05-30-2012, 02:13 PM
ImpalerCore
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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.
Reply With Quote
  #23 (permalink)  
Old 05-30-2012, 02:25 PM
nick_keighley_nospam@hotmail.com
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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.


Reply With Quote
  #24 (permalink)  
Old 05-30-2012, 03:37 PM
Ben Bacarisse
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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.
Reply With Quote
  #25 (permalink)  
Old 05-30-2012, 04:26 PM
Willem
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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
Reply With Quote
  #26 (permalink)  
Old 05-30-2012, 05:05 PM
Johann Klammer
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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
Reply With Quote
  #27 (permalink)  
Old 05-30-2012, 05:29 PM
Kulin
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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!



Reply With Quote
  #28 (permalink)  
Old 05-30-2012, 08:57 PM
mike3
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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 a
handler 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.
Reply With Quote
  #29 (permalink)  
Old 05-31-2012, 08:10 AM
Mark Bluemel
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, 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!
Reply With Quote
  #30 (permalink)  
Old 05-31-2012, 08:19 PM
Tim Rentsch
Guest
 
Posts: n/a
Default Re: Error returns and repeated code, GOTOs, etc.

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.
Reply With Quote
 
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are Off
Pingbacks are Off
Refbacks are Off




All times are GMT. The time now is 03:45 AM.


Copyright ©2009

LinkBacks Enabled by vBSEO 3.3.0 RC2 © 2009, Crawlability, Inc.