   Exception handling in games + error codes. Some tips needed.
  Hi,I will start in the next days a new project. All my life have used error codes for initing/ending objects in c++. Now I would like to get into RAII and use exceptions + my all good error codes for non excepcional errors.I mainly will use them for constructing/destroying objects, so I think it is a good idea to use auto_ptr to follow RAII more easily.Could anyone give any tip about the structure of the exception handling? I mean where do you ussualy use the try/catch, etc... ? I think I will be using at first a global try/catch and refine later as I need, but not sure if this is a good aproach.By the way, I have been reading/googling for some docs related to use exceptions in games and I couldn´t get anything good, so I decided to post this.Thanks in advance,HexDump.   
  The C++ exception handling mechanism is designed to allow errors to be caught where they can be corrected. The problem with the traditional "return error code" approach is that the function that can fix the problem might be 10 levels up in the call stack. That means that every function between the function raising the error and the function catching it will be required to return an error code. So to sum it up, exceptions should be caught where the error can be handled.As for auto_ptr, it suffers from a pretty nasty problem: it's copy constructible, but does not implement any form of reference counting. As such, it's entirely possible to initialize an auto_ptr, pass it to another function, and then have your program crash as soon as you try to use the auto_ptr again. That's because the function you passed it to created a copy, which when it went out of scope deleted the memory pointed to by the auto_ptr (not knowing that there was another reference). (see SiCrane's post) A better alternative is Boost's scoped_ptr (for single reference) or shared_ptr (for multiple references).[Edited by - Windryder on May 6, 2010 3:35:48 PM];Quote:As for auto_ptr, it suffers from a pretty nasty problem: it's copy constructible, but does not implement any form of reference counting.This is by design, though, and while it does mean that auto_ptr is often nonsuitable for general purpose smart pointer use, it is perfectly usable in the scenarios for which it was designed.Quote:Could anyone give any tip about the structure of the exception handling? I mean where do you ussualy use the try/catch, etc... ? I think I will be using at first a global try/catch and refine later as I need, but not sure if this is a good aproach.A global try/catch in main() or some such is useful for debugging -- in the case where you failed to catch the exception anywhere else, you can catch it there and print some diagnostics. In general, however, you don't want to allow that to happen. The purpose of exceptions is allow errors to be propagated to the point at which they can be correctly handled and recovered from. You should generally catch an exception at the closest point to the throw site that you can reasonably handle and recover from it.That said, however, exceptions are not a substitute for error codes -- both have their merit and their proper uses, and those things don't overlap entirely. Exceptions in C++ should generally be used to indicate critical, unexpected failures, whereas return codes should be used to indicate failures that you expect to happen during nominal and reasonable execution of the program.   ;  Exceptions should generally be used for unrecoverable errors. - Losing connection to a server is unrecoverable - a new connection might be established as part of exception handling.- Reading a file, where header specifies 550 bytes length, but only 340 are available is also exceptional condition - file is corrupt- Reading a packet from user and encountering incorrect or unexpected value. For one reason or another (broken client, malicious intent) the peer is broken. Exception indicates we no longer want to talk to them, no matter why or how this exception occurred.- Invariants (aka RAII): an object may be either fully and completely constructed, or not at all. If initialization fails, we cannot do anything about it. Applies to memory allocation as well (new, malloc).There are many recoverable errors which are frequently implemented as exceptions, even if that isn't desirable:- Running out of resources (disk, memory, handles) in general should not be unrecoverable. Running out of disk space while storing user's 1000 page document must be an expected and well handled situation, regardless at which stage it occurs. Precondition: user modifies a document - post-condition: changes were confirmed to have been written to disk. Any other scenario is not acceptable. Simple rule: all resources are finite - hence not unrecoverable but well anticipated. Most languages today rely on luck hoping that machine on which software runs will provide enough resources for this to not be a problem.- Validating user input. If user is able to type in characters that might not match desired input, then this is recoverable and expected.There is third school of thought: NullObject. It is used in place of invalid/null value. NullObject for list is an empty list. NullObject for string is empty string. For Renderer, it's a NOP implementation, where all operations can be called, but don't do anything. For configuration, it's empty config file.   ;Quote:Original post by WindryderThat's because the function you passed it to created a copy, which when it went out of scope deleted the memory pointed to by the auto_ptr (not knowing that there was another reference).That's not how auto_ptr works. When a copy of an auto_ptr is created, ownership of the held object is transfered to the new auto_ptr and the original auto_ptr no longer refers to the held object. Hence, there isn't another reference.;Quote:Original post by SiCraneThat's not how auto_ptr works. When a copy of an auto_ptr is created, ownership of the held object is transfered to the new auto_ptr and the original auto_ptr no longer refers to the held object. Hence, there isn't another reference.My bad. Regardless, the consequences are pretty disastrous.;  That's like saying the consequences of using delete are pretty disastrous. If you use it wrong, then you've got problems. If you use it right then you don't. And in the case of auto_ptr it's pretty easy not to transfer ownership if you don't want to: just don't pass it by value.   ;Quote:Original post by SiCraneThat's like saying the consequences of using delete are pretty disastrous. If you use it wrong, then you've got problems. If you use it right then you don't. And in the case of auto_ptr it's pretty easy not to transfer ownership if you don't want to: just don't pass it by value.I personally wouldn't pass it to anything at all.   ;  It depends on your use cases, obviously, but one relatively common thing I do pass auto_ptr's to are shared_ptrs. shared_ptr has a special constructor just for auto_ptr arguments which transfers ownership to the shared_ptr.   ;Quote:Original post by SiCraneIt depends on your use cases, obviously, but one relatively common thing I do pass auto_ptr's to are shared_ptrs. shared_ptr has a special constructor just for auto_ptr arguments which transfers ownership to the shared_ptr.That's true, and in those cases where transfer of ownership in intended then passing an auto_ptr will make that intent explicit, which is a good thing.   