Jump to content
  • Advertisement
Sign in to follow this  
Mizipzor

Errorcheck in class constructor

This topic is 4806 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I did just start coding on my first graphic engine :). 2d tilebased. Im using SDL, which I need to initialize, which I do in another function of the class called initSDL(). However I want to check if it was initialized or not, but the constructor cant return false. If I dont have graphics I want the application to completely shutdown. Any suggestions on how to do it? I guess I could decide not to call the iniSDL from the constructor and call it from main() instead, and if doesnt return true, main returns 0. But I want as much as possible too happen within the functions themselves. Ideas?
Graphic::Graphic() {
	if(!initSDL) {
		cout << "Unable to init SDL: " << SDL_GetError() << endl;
		// complete termination of application
        }
}


Share this post


Link to post
Share on other sites
Advertisement
You could certainly give Exception handling a try here.

If Unable to init SDL, open a log file, throw in an error statment and debugging code, then shut down the program graceful, deallocating all your memory.

That may be excessive for this problem. Anyone have a better, simpler, answer?

Share this post


Link to post
Share on other sites
But how do I tell the application to nicely shutdown? Im going to create this graphic class from the "master" class, the game main class, which handles all the communcation between the classes and the main loop.

How do the masterclass tell if the Graphic class's contructor failed or not?

Share this post


Link to post
Share on other sites
In the Graphics ctor, you throw the exception if something goes wrong.

In the "master" class, attempt the ctor in a try{} block, and catch{} the exception, shut down nicely from there.

However, consider the possibility that a class is not the right tool for abstracting "Graphics" or "Main".

Share this post


Link to post
Share on other sites
As Zahlman so graciously pointed out. You would do something roughly like this psuedo-code:


main()
{
graphic graphicobject
try
{
graphicobject.init();
}
catch (...)
{
//graphic class not created
//log reason
//clean up memory
}
//graphics class was created, go about your business
}




and your graphic class constructor would have something like this:


graphic::graphic()
{
try
{
//allocate memory
}
catch (...)
{
//unable to allocate memory
//leave constructor
}
//memory allocated succesfully
//initialize the object
//whatever else
}





So, what you end up with is if everything goes well, the program works just fine. If allocating the memory does not work, then the problem is caught (without crashing the program) and the problem is reported back to the main function. Where you can handle the problem gracefully.

Share this post


Link to post
Share on other sites
Thanks for the replies :) I'll look up some tutorials on how to do it that way and see what I can figure out.

Zahlman, how would you how done it? are there better ways to do the stuff that I want to do?

[edit] Ive been searching around and I found the terminate() function, which kills the program from whereever it wants within the program. So why is this:


Graphic::Graphic() {
printf("Initializing SDL\n");
try {
if(!initSDL())
throw SDL_GetError();
}
catch (string error) {
printf("SDL_Init failed: %s", error, "\n");
terminate();
}



better than this:

Graphic::Graphic() {
printf("Initializing SDL\n");
if(!initSDL())
printf("SDL_Init failed: %s", error, "\n");
terminate();
}
}



Im using SDL so all printf go to a file instead of onto the screen, which gives me that log file.

[Edited by - Mizipzor on June 20, 2005 6:50:57 PM]

Share this post


Link to post
Share on other sites
the run-time terminate function calls the abort() or exit() functions. There is a very good reason not to do this. The process exits immediately and no memory is deallocated.

Check out this quote from DevX.com, it explains better then I can.
http://www.devx.com/tips/Tip/12465

Quote:

Expertise: Intermediate
Language: C++
April 10, 1998
How to terminate a program safely?
In standard C, the functions abort() and exit() perform an immediate program termination, regardless of where they are called from. Although this also works in C++, it is usually not a good idea to terminate a program this way, even when a severe error has occurred. The problem with these functions is that none of them ensures that local objects' destructors are called: abort() calls no destructors at all and exit() invokes only the destructors of objects in its scope (but not in its caller). You can imagine the disastrous results when destructors of objects holding open files handles, dynamically allocated memory, mutexes, etc. are not called. A better choice is to throw an exception. The C++ Standard guarantees that when an exception is thrown, all local objects' destructors are called. If no handle for that exception is found, the program will terminate but only after all local objects in the entire calling chain have been invoked. Mind also that exceptions can give the handler a chance to fix the problem and continue, whereas abort() and exit() are irreversible.
Danny Kalev

Share this post


Link to post
Share on other sites
Quote:
Original post by Mizipzor
[edit] Ive been searching around and I found the terminate() function, which kills the program from whereever it wants within the program. So why is this:

*** Source Snippet Removed ***

better than this:
*** Source Snippet Removed ***



::sigh:: It isn't - if anything, it's worse. The idea is for the constructor to *just* do the throwing, and the *caller* checks try-catch.

As for other forms of organization...


// Graphics.h
#ifndef GRAPHICS_H
#include GRAPHICS_H
namespace Graphics {
void init();
}
#endif

// Graphics.cpp
#include "Graphics.h"
namespace Graphics {
void init() {
if(!initSDL()) {
cerr << "Unable to init SDL: " << SDL_GetError() << endl;
std::terminate();
}
}

// main.cpp
#include "Graphics.h"
int main() {
Graphics::init();
}



Now you don't have "Graphics instances", but you do still have the option of encapsulating things properly. Data and functions of the Graphics "module" get their own namespace to prevent accidental name collisions, and if there are data (most if not all) or functions (usually some) that you don't want outside callers to touch, you just *don't mention them in Graphics.h*, problem solved. On the other hand, you do still have responsibility for making sure that init() gets called, and at an appropriate time.

You can acheive a similar effect with the use of static functions and data within a class - such data is "shared by all instances", and also can be referred to from the static functions, which don't need to be invoked on an object (but can instead be used as with the given namespace illustration). Of course, you don't get polymorphic behaviour that way, and you *still* have to make sure things are initialized at an appropriate time (and possibly "torn down" too).

In Java, for comparison, there is no direct equivalent of namespaces, but you can still dump static stuff into a class. It works probably more nicely in Java because there are better guarantees about the order in which static stuff will be constructed, and for classes you can write a "static initializer" block which will be executed "the first time the class is referred to". C# borrows these ideas too AFAIK.

Anyway - as for std::terminate(), it's all a matter of just *how* fatal the condition is :) Failure to init SDL is probably going to prevent you from doing anything more, yes. But std::terminate() is kind of "rude", in the same way that the old C exit() is, and probably should only be a last resort. Keep in mind that an exception that doesn't get caught *at all* (and remember that it will go back through the "call stack" all the way looking for an exception handler) will also terminate the program - quite possibly using std::terminate() or something similar (I'm not too well-read on that part of the standard).

Share this post


Link to post
Share on other sites
Zahlman is correct, the two samples are identical in behaviour.

You should (almost) never call terminate at the point of error, though - it's not nearly as flexible as throwing an exception. An uncaught exception will terminate the program, which is what you're looking for.

So, to sum up,

bad, because it is inflexible and difficult to change later:

Graphic::Graphic() {
if(!initSDL) {
cout << "Unable to init SDL: " << SDL_GetError() << endl;
std::terminate();
}
}




pointless, because it's identical to the above:

Graphic::Graphic() {
printf("Initializing SDL\n");
try {
if(!initSDL())
throw SDL_GetError();
}
catch (string error) {
printf("SDL_Init failed: %s", error, "\n");
terminate();
}




MUCH better way of doing things:

Graphic::Graphic() {
printf("Initializing SDL\n");
if(!initSDL())
throw std::string(SDL_GetError());
}

int main()
{
try{
//...
Graphic graphics;

//...
}
catch(std::string& error)
{
std::cerr << error << std::endl;
}
}




more expressive than that:
//graphic.h
#include <exception>
class Graphic
{
public:
//...

Graphic();

//...

class Exception : public std::exception
{
Exception(const char* message) : exception(message)
{}
/*this just tells Exception's constructor how to call its base
class's constructor (std::exception).*/

};

private:
//...
};




//graphic.cpp
Graphic::Graphic() {
if(!initSDL) {
throw Exception(SDL_GetError());
}
}




//main.cpp (for example)

int main()
{
try
{
Graphic graphics;

/*or we could call some other function which itself creates the
Graphic object. or it could even be a member of a class.*/

}
catch(Graphic::Exception& error)
{
//we know the error came from Graphic
std::cerr << "Graphic error: " << error.what() << std::endl;
/*.what() is inherited from std::exception, and returns
the error message we wrote in the constructor.*/

}
catch(std::exception& error)
{
/*if we didn't have the above catch block, we could rely on
this one instead since Graphic::Exception is derived from
std::exception.*/

std::cerr << "Error: " << error.what() << std::endl;
}
}


Share this post


Link to post
Share on other sites
If you're going to use exceptions in your code you should always write exception safe code. A very good book about exceptions is "Exceptional C++" from Herb Sutter and his follow-up book "More Exceptional C++"

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!