exceptions

Started by
74 comments, last by aboeing 20 years, 6 months ago
You will often end up with a lot of small objects. But I still think this is the best way to do it, and especially so if creating a B object involves operations X, Y, and Z with some kind of transactional semantics. How do you get resource leaks if you do that?
Advertisement
class myclass{   public:     myclass(pointer* ptr)     {        if(ptr == NULL) throw exception(0);     }};int main(int, char**){   myclass* m;   try   {      m = new myclass;   }catch(const exception& e)   {     report("error!";   }   return 0;}[/source]   
//--------------------------------------------------// typical library code//--------------------------------------------------bool myfunction(){    if(!anotherfunction())     return false;    return true;}bool anotherfunction(){   if(!yetAnotherfunction())       return false;   return true;}bool yetAnotherfunction(){    if(!yougottabekidding())       return false;     return true;}bool yougottabekidding(){   if(global_X > global_Y)      return true;   return false;}int main(int, char**){   if(!myfunction())   {     report("Error");     return 0;   }   return 1;}


//---------------------------------------------------// better way _with_ exceptions//---------------------------------------------------void myfunction(){    anotherfunction();}void anotherfunction(){   yetAnotherfunction();}void yetAnotherfunction(){    yougottabekidding():}void yougottabekidding(){   if(global_X <= global_Y)      throw exception(OKNOTTHEBESTEXAMPLEBUTHEY);}int main(int, char**){    try    {      myfunction();    }catch(const exception& e)    {      Report("error");      return 0;    }    return 1;}


These examples clearly shows when to use exceptions in my opinion.
Well, if you don''t want to use exceptions I strongly suggest you to at least do this :
int WINAPI WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int) {    try {        everything    }    catch(...) {        // so nothing or something    }    // we WILL come here    try {        Cleanup();        // and call memorymanager, basicly cleanup after yourself    }    catch(...) {        // if THAT fails, there is nothing you can do    }}


As someone stated before, exceptions should not be used to redirect your flow conrol or to replace error codes. When writing program(single *.exe) of few thousands lines, exceptions are not neccessary. But if you have several modules(engine.dll, core.dll, app.exe, game.dll, musi.dllc, network.dll, ...) error codes are just not it.

Exceptions are logical addition to high level of abstraction. In C, functions pretty much did themselves, but in C++ you have higher levels of abstration. everything is abstracted by design patterns, template functions, smaller frameworks for doing small things. Getting out of deep nested functions is painfull with error codes. However, error codes should be used but for indicating succession of some function.

My example:
// Function LoadLevel should deal with all errors that may arise.// Function that calls LoadLevel needs to know wheather or not level could be loadedbool LoadLevel(/*some parameters*/) {    try {        // lots of code here    }    // specific exceptions    catch(Err::EFileNotFound &e) {        return false;    }    catch(..) {        return false;    }    return true;}

Problem is that most programmers don''t realize who should contain try-catch block. Try-catch block should be in that function which can handle exceptions that are raised. They are raised not because it is an exceptional error, but because current situation cannot be resolved in that function, but in function with more information about current state. You cannot post good example because exceptions are used in complex environments.

try blocks don''t involve any overhead on nowdays computers. After I check Unreal public source code, I implemented guard-unuard block in majority of my functions. I haven''t lost a single FPS(Celeron 535 Mhz).

Exceptions are not meant to be used in small-scale projects, but in large complex environments. For those environments, error codes are sufficient.

About that vector<> class issue, out_of_bounds exception should be thrown because your program should never access element that does not exist. When this happens, something is wrong with you program.
So... Muira Yoshimoto sliced off his head, walked 8 miles, and defeated a Mongolian horde... by beating them with his head?

Documentation? "We are writing games, we don't have to document anything".
How do you get resource leaks if you do that?
The old multiple new problem thats been discussed previously, the solution was given, but I just think its more of a pain than just having a seperate initialization function or something.

These examples clearly shows when to use exceptions in my opinion.
Your really just using exceptions to notify a higherlevel object of a failure from somewhere else, I dont really see the need to use exceptions to do this, other than potential threading issues.

My example: ....
Yeah thats the way Ive pritty much decided to use them so far (so that none of the classes I write will throw exceptions, they will just catch them from other things that I use, and return error values - this seems like the most sensible thing to do as far as I can tell). But like you say it will be difficult to know where the try/catch blocks should go.

You cannot post good example because exceptions are used in complex environments.
Yeah, again I''ve realised this, and so thats why Im thinking of doing everything using something similar to the macro''s I''ve shown, that way I can always change my mind half way through the project (I hope!)

Since people have stoped responding to this thread as much, I would just like to say thanks to everyone for thier input, I''ve learnt a lot about exceptions, there seem to be a lot of different opinions about how they should be used, so I guess I will just have to try things out and see how they go.
Cheers.
quote:Original post by aboeing
The old multiple new problem thats been discussed previously


There is no problem.
struct B{    B(int v1, int v2) : v1_(new int(v1)), v2_(new int(v2))    {    }        void do_something_with_v1() {}    void do_something_with_v2() {}private:    std::auto_ptr<int> v1_;    std::auto_ptr<int> v2_;};


Again, RAII to the rescue. Both v1 and v2 will be destroyed if B() throws. If v2(new int(b)) throws, v1 will be destroyed. If you don't want to use smart pointers then:

struct B{    B(int v1, int v2) : v1_(0), v2_(0)    {        try        {            v1_ = new int(v1);            v2_ = new int(v2);        }        catch (...)        {            delete v1_;            delete v2_;            throw;        }    }    ~B()    {        delete v1_;        delete v2_;    }    void do_something_with_v1() {}    void do_something_with_v2() {}private:    int* v1_;    int* v2_;};


quote:Isn't this a better method than acquiring resources on construction?
...
but I just think its more of a pain than just having a seperate initialization function or something.


I can only repeat what has already been said. Separating initialization from creation forces B to have a default behaviour. This imply that, until B is initialized, it must prevent any operation which involves a resource, and report the failure by either return values or exceptions. If initialization fails, it must revert to its default behaviour.

struct B{    B() : v1_(0), v2_(0) // default state    {    }    ~B()    {        delete v1_;        delete v2_;    }    void init(int v1, int v2)    {        if ( v1_ )            // already initialized: throw, return, reinitialize?                try        {            v1_ = new int(v1);            v2_ = new int(v2);        }        catch (...)        {            // initialization failed            delete v1_;            v1_ = 0; // return to default state            delete v2_;            v2_ = 0;            throw;        }    }    void do_something_with_v1()    {        if (!v1_) // throw or return        ...    }    void do_something_with_v2()    {        if (!v2_) // throw or return        ...    }private:    int* v1_;    int* v2_;}; 

So, if a default behaviour is not required or meaningful , why go through all the hassle of extra error handling and if (!resource) ..., just so initialization can occur outside the ctor?


[edited by - Matsen on October 19, 2003 1:00:07 PM]
quote:Original post by aboeing
These examples clearly shows when to use exceptions in my opinion.
Your really just using exceptions to notify a higherlevel object of a failure from somewhere else, I dont really see the need to use exceptions to do this, other than potential threading issues.


An issue you havent considered, what about functions which dont return anything? with exceptions you dont have to return anything apart from when you throw, with the other system you are forced to always return to get any meaningfull infomation out of the function.
This removes the burnen of haing to manualy propergate the error codes back up via multiple returns of data and allows you to return more infomation (return of 1 var vs an object with multiple bits of infomation)

This topic is closed to new replies.

Advertisement