Advertisement

Error handling: Opinions?

Started by September 27, 2000 02:29 PM
43 comments, last by Aienthiwan 24 years, 3 months ago
Ok,

I see. Well, I understand now more of the limitations of try/catch/throw. Thanks for all your suggestions.

Aienthiwan
(sorry about bumping the thread)

JOL:
quote:
Exceptions are not a good way of dealing with errors that might occur when your algorithm isn''t perfect, then you might wan''t some kind of debug/error system that could be deleted for the release version(macro or #ifdef or something).


ASSERT! Assert rocks. I believe there''s an assert thread occurring at this very moment in this forum. I don''t know much about the std::assert because I work almost exclusively in MFC, so I use the MFC ASSERT(). It''s saved my butt many a time. Assert is your friend.
Advertisement
This can have been said allready, cuz i haven''t read the whole reply list. But anyways.

using try/catch blocks is superior if your function suceeds most of the time, that is, if the function usually doesn''t fail use try and catch.
But never use it for return values.
Ok, you ppl seem to be timing crazy so here goes in rations:

calling function that doesnt return value and doesnt throw = 1

checking return value = 1.48
try/catch = 1

note that above is the avarage of 10 iterations calling each function 1 million times each, and never actaully getting an error. Ok, not a very good scenario but the timings are actually the same using lower numbers. So checking the return value can cost you about 50% in the time it takes to determine an error.
Probably not a very big deal if you have big functions, but note that try/catch is more efficent.

The bottom line is, use try/catch if your function most of the time doesnt get an error, and if it does it''s probably broken anyway , so the return 0 / return ErrorCode stuff should be placed where it belongs in the recyclebin. Hey try/catch even makes your code cleaner. Not often you get so many benefits for free now is there.

/me.. send flames to nalle@thearmy.com
Ok,

Lemme ask you this: With the DirectX situation, how do you intend to know when to throw an error when you have to check to see if it''s non-zero in the first place?

ie:

HRESULT Hr = DirectXFunctionFoo(SomeArguments);
if (Hr) throw cDxException(Hr, __FILE__, __LINE__);

In this case, I would think it''s less effecient here, since you have to check for the return value, then throw if actually is an error. I would put this is a macro BTW.

So thus you''re doing 2 jobs, error checking and exception throwing. Comments?

Aienthiwan
The reason it is still more efficient than returning error codes, is when you are multiple functions deep. For example, you have an InitClient function, which calls InitGraphics, which calls InitPrimarySurface. If InitPrimarySurface function returns an error, then you must check that in InitGraphics and return an error from there, and check that in InitClient then you realize you need to shutdown the app.

However, if you throw an error from InitPrimarySurface, and your try/catch is in InitClient then when you throw the exception you are automatically taken back to InitClient. You don''t have to worry about checking return values for every function.

I myself am still hesitant on using try/catches, because you have no control over program flow. You can be calling a void function, thinking you''ll be executing the next line, but instead you are taken to somewhere completely different in your program because that function threw an exception (which you may not even care about processing). I mean, isn''t that the reason goto''s are frowned upon, because it interrupts the natural flow of a program and is confusing jumping around code left and right?

- Houdini
- Houdini
quote: Original post by Houdini
I myself am still hesitant on using try/catches, because you have no control over program flow. You can be calling a void function, thinking you''ll be executing the next line, but instead you are taken to somewhere completely different in your program because that function threw an exception (which you may not even care about processing). I mean, isn''t that the reason goto''s are frowned upon, because it interrupts the natural flow of a program and is confusing jumping around code left and right?


No. Exceptions (normally) signal exceptional behavior. Which means, if you can''t handle it, you shouldn''t continue processing, as your code relies on the system to be in an un-exceptional state. If you don''t care if the function throws or not, just wrap it in a try/catch(...) block.

If you don''t want to write code that way, use asserts.

MSN
Advertisement
quote: Original post by Aienthiwan
In this case, I would think it''s less effecient here, since you have to check for the return value, then throw if actually is an error. I would put this is a macro BTW.


As opposed to checking the return value and doing something constructive if it does not signal an error. In either case, you check the value and continue if it''s not an error.

Don''t try to optimize until your code is correct.

And BTW, since we''re in C++ land, don''t use macros. They are extremely ugly and can lead to undesirable behavior.

(As with most rules, this one can be broken, but only when you know exactly what you''re doing. Which is almost never the case. Case in point, throwing for abnormal but not exceptional behavior. Throwing a return value from a function can be worthwhile, but it depends entirely on the context.)

A much more C++ compatible check (and readable, IMO) is to define a class HRCheck that has an operator<< overload for HRESULTS that throws if the FAILED(HRESULT):

hr << pDDS->Flip(); // throws if Flip fails

MSN
re: not using macros in C++

I agree with you if you don''t want to send __FILE__ and __LINE__ with the function. In that case, something like this is appropriate:
inline void DXWrap (HRESULT res)
{
if (res) throw runtime_error ("Failed call");// or whatever
}

If, however, you want to use __FILE__ and __LINE__, it''s more appropriate to do this:
#define DXWRAP(res) DxWrapInternal (res, __FILE__, __LINE__)
...and then define an inline DxWrapInternal that will check res and create a meaningful error string if there''s an error.

Like someone (I forget who) here said, everything in C++ is there for a reason. Sometimes even "goto" is arguably appropriate.
I think what people *really* need to be doing is developing some sort of rollback scheme for when an error occurs. eg if you have loaded 100 textures, and the 101st texture load generated an error, you obviously dont want the program just writing the error and bombing out, leaving those 100 textures in memory.

This is a simple example, but imagine all the data structures/data that is loaded along the way to an error, and I think you will find that you have 2 choices:

a) Forget all that data and bomb out, leaving a large amount of system resources locked up to nothing (AKA easy way)
b) Make a system that records a list everything that is loaded/initialized which needs to be destroyed, and if an error occurs (which is caught or detected somehow), iterate through the list of objects created, deallocating/destructing them (AKA the hard way)

I am yet to successfully pull of a system like b), but perhaps that is where the focus should go...

Cel
Cel aka Razehttp://chopper2k.qgl.org
Cel, have you heard of the phenomena called ''stack unwinding''?

Consider:
void Class::loadTexture(File &file){    Texture texture;    if(texture.load(file) == ERROR_VALUE){        throw TextureLoadFailedException("Couldn''t load texture");    }} 


When an exception occurs all local objects are destroyed in the reverse order of creation. That being the case, no memory leaks will be caused by the exception in the example above.

For dynamic objects (pointers to objects) you need to do something like this:

void Class::loadTexture(File &file){    Texture *texture = new Texture();    try {        if(texture->load(file) == ERROR_VALUE){            throw TextureLoadFailedException("Couldn''t load texture");        }        // other stuff    }    catch(Exception &exception){        delete texture;    // release memory        throw;             // continue exception    }} 

-------homepage - email

This topic is closed to new replies.

Advertisement