Errorcheck in class constructor

Started by
8 comments, last by ext 18 years, 10 months ago
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
        }
}


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?
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?
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".
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.
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]
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
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_Hnamespace 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).
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.cppGraphic::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;	}}
-------------"On two occasions, I have been asked [by members of Parliament], 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?' I am not able to rightly apprehend the kind of confusion of ideas that could provoke such a question."- Charles Babbage (1791-1871)
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++"

This topic is closed to new replies.

Advertisement