Advertisement

Error Handling

Started by February 12, 2000 11:00 PM
31 comments, last by Daniel Benmergui 24 years, 7 months ago
Hello Again!

To _dot_:
I am thinking that i have a glitch in the design too, but i cant seem to discover it. I didn''t wrap the surfaces in a class because beside the automatic release provided by the destructors, it isn''t very useful. Most of the time i would be doing direct mapping (i don''t know if i translated that right . I use wrapper functions instead (to load bitmaps, etc.) and let main() take care of the surfaces. Maybe the glitch is around that.

I now have a doubt about lost surfaces...
will Blt report SURFACELOST only if the destination surface is lost, or the source too?





Daniel:

The best solution I´ve found was to implement a function that receives a string (as a printf, so you can pass any arguments), and append it to a log file (or send it to the debugger). I´ve finished a game, and I must tell you this is easy, comfortable, realiable, powerful _and_ elegant. It´s very easy to implement, too. If you want the source code, just say it to me (10-15 lines of code)...

El juego se llama Malvinas 2032, y esta a la venta en Compumundo, espero que lo compres y me digas que te parece. Ponete en contacto asi vemos si podemos hacer algo en conjunto.

Despotismo AKA Javier OtaeguiSabarasa Entertainmentwww.sabarasa.com.ar
Advertisement
Creating a log file is fine...that is the best way to record an error and don''t show it to the user. But i still have the dilemma about how to handle it...

Let''s suppose that i have a class Log
with a member function Entry(char*) that records a string in disk.

I can think of three ways to handle it:
1) return values:
With that, i have to problem i previously mentioned about losing information. If the Log is in Main(), the log would report:"Tileset:sprite failed" and i wouldn''t know that it was a lost surface!

2) Exceptions:
I didn''t experimented with it yet, but i read the chapter at Stroustrup. If i understood well, this is what is going to happen:

class Sprite tries to blit, and DDraw returns an error value
Sprite throws an exeption:DDERR_SURFACELOST(surface)
class tileset traps it. Don''t know what to do with it, so it send it up.
main() traps the error. Ok, main will know what to do with the surface.

This doesn''t seem to be a comfortable way of handling errors...

3) Putting a pointer to Log in each class
So when an error pops up, it is instantly logged. This is okay, but it won''t help me recover.

Imagine this:

Main ()
{
LoadBitmap(lpSurface,"bitmap.bmp");
Tile.SetSourceSurface(lpSurface);
Tileset.addTile(&Tile);
Tileset.Render(lpPrimary);
}

As you can see, i handle surfaces in main, and not in tile, or tileset. What do you think of this approach?
Note: My design doesn''t work exactly like that, i just made an example for the subject.


I''d say it depends on if you really are handling the error or not.

In the case of lost surfaces which are then restored, my class wrappers for a DXSurface include an object specifically for restoring a surface, whether it is re-reading a file or re-blitting a picture or whatnot can be defined by that lost-surface handling class.

Then, in this case it really is not an exceptional condition of the class, just an error condition of the DX blit routines.

As far as truly exceptional conditions... I''d have to say that if you are NOT handling the error and continuing with normal execution than you should let it propagate up to the component which initiated the action. As that is the only point of flow control that can make the decision about what to do next.

As far as logging, I''ve writting some simple exception classes for translating DD errors into human readable strings that has a simple method for logging the exception which gets called in the constructor of the concrete exception class.

This way I can easily use exceptions when conditions prevent me from executing further and I get a log file as well.


All logically designed components should be self-sufficient, meaning handle errors automically.
As you said, as it bubbles up the function call you would have difficulties in handling them, as the possibilities of errors built up.
Handle where it occur, and throw an error only when it''s unrecoverable. In the meantime, try/catch every exceptions, and perhaps create an exception class. When caught, append the function call name, so you will know the trace of function calls, and when throwing, throw a string value. Finally, output to a file.
Hello!
_dot_, tell me more about how this object you did that handles lost surfaces works...
Advertisement
It isn''t anything complicated. It''s just, when a failed Blt/BltFast returns a surfacelost, just do a call the restore surface function, and maybe reload the file.
You can most likely see such stuff in Microsoft samples like restoreSurface function or whatever.
Hmmm...
Come to think of it, maybe you might want to have an activate/deactivate function attached to each of these objects, so that an activate would check if this is a lost surface, and restore it. It would most likely be called during WM_ACTIVATEAPP.
I understand...but i come up again with the same question i asked above...
Can Blit return DDERR_SURFACELOST for the source surface?
because if that is the case, the matter is more complicated.
You can restore it, but you can''t reload it if it is a composed surface.(composed=made out of blits from other surfaces).
Ok. Here's my approach.

Every class i create is responsible for its native error handling/reporting.
Every class has the following code:
char *ErrMsg;

When an error occures, i write about it there, and change the return code. The ErrMsg is for notifying the user, and the return codes for notifying the application itself.

Each function before it exits, appends the classes local ErrMsg the ErrMsg that appears on the function they called.
------------------------------------------------
Assume ClassA, ClassB, ClassC

MainProgram
int rVal=ClassA->Something();
if(rVal!=err_OK){
strcpy(ErrMsg,"Error - ClassA->Something ;; \n");
strcat(ErrMsg,ClassA->ErrMsg;
ReportError(ErrMsg); //Your function to log errors...
}
}

ClassA::Something(){
int rVal=ClassB->Something();

if(rVal!=err_OK){
strcpy(ErrMsg,"Some error occured in ClassA::Something. Error originator replied: \n");
strcat(ErrMsg,ClassB->ErrMsg);
return rVal;
}

return rVal;
}

ClassB::Something(){
int rVal=ClassC->Something()
if(rVal!=err_OK){
strcpy(ErrMsg,"Some error occured while doing Something in ClassB. Originator ClassC::Something replied:\n");
strcat(ErrMsg,ClassC->ErrMsg);
return rVal;
}

return rVal;
}

int ClassC::Something(){
//Here i know some error has occured...
strcpy(ErrMsg,"Error in ClassC::Something(), Line..\n"); //This ErrMsg resides in the ClassC class
return err_BadBlock; //Some error code i've defined
}
--------------------------------------------------
I know that something like this will increase the size of code, however i recomend it for easy and fast debugging. After all, this is what i do myself.

PS: The best solution is return codes. Exceptions are good for trapping errors generated by the CPU (the only way to get 'em ).


Hope this helps.
c'ya

Edited by - Harvester on 2/15/00 8:19:54 AM
... LEMMINGS ... LEMMINGS ... LEMMINGS ... LEM..... SpLaSh!...Could this be what we stand like before the mighty One?Are we LeMmIngS or WhAt!? ;)
Uhm.. I think I''m the only person here who champion the unheard of use of ''goto'' statements. I know I''m going to get flamed, but there''s nothing wrong with them. Look at this example:

ERROR_Type DDRAW_Blt(Surface *surface, int x, int y)
{
ERROR_Type status;

status = g_pBackBuffer->BltFast(surface ... );
if (status != DD_OK)
{
if (status == DD_SURFACE_LOST) // or whatever
status = ERROR_DDRAW_SURFACE_LOST;
else
status = ERROR_DDRAW_BLTFAST;

goto exit;
}

exit:
return status;
}

Then in my main loop I can do

ERROR_Type GameMain()
{
ERROR_Type status;

status = DDRAW_Blt(g_surface, 1, 1);
if (status)
{
if (status == ERROR_DDRAW_SURFACE_LOST)
g_surface->Restore();
else
goto exit;
}

.. do the rest

exit:
return status;
}

Then when GameMain() returns I can do this:

ERROR_Type status;

...

status = GameMain();
if (status) goto exit;

exit:
printf("ERROR: %s (%d) \n", FindErrorStrings(status), status);


I have an FindErrorString function that loops up the status and finds the string to report.

I really like this method of using error codes looked up in a table - first of all it makes all your test strings centralized so adding things in and making changes is easy instead of hunting and picking. Also it makes your error reporting more consitent. I hate it when a program reports the same error as "Bitmap load error", "Couldn''t load bitmap", "Bitmap!!" and "ERROR - bitmap a loaderror".. This comes from using strings throughout the program instead of numbers..

As for the goto statements I like the error reporting chain it creates, and having the exit: tag makes cleanup easy:

functionA()
{
ERROR_Type status = ERROR_NONE; // 0
char *data = NULL;

data = malloc(1000);

status = LoadData(data);
if (status) goto exit;

PerformFunctionsOnData(data);

exit:
if (data) // data was allocated (not NULL)
free(data);

return status;
}

This example guarantees that data will always be freed, but won''t PerformFunctionsOnData() if the LoadData was unsuccessful.. this works great when multiple steps need to take place but you want an ''easy out'' if an error occurs.

Anyone telling you this isn''t efficient coding is lying. Exceptions, returns and multiple nested (and ugly!) if statements are all jumps. The goto is just honest about it.

Anyways.

Works for me.

And the fact you can do error checking in switch() statements for how to react to a certain error is nice.

This topic is closed to new replies.

Advertisement