Reputation: 683
So, basically I have this simple wrapper code for an external C library and I'm a newbie in terms of proper exception handling.
In advance: The code shows the same problem two times, but maybe there's a different solution for the class version.
#include <some_c_lib>
void setup(){
//some setup code
//init function from the C library
//with C-style return code for error handling
if(!init()){
//error: program should terminate
//because error cannot be handled
}
//some setup code
}
class myclass{
//some members
public:
myclass(){
//some construction code
//create function of the C library
//with C-style return code error handling
if(!create()){
//error: program should terminate
//because error cannot be handled
}
}
~myclass(){
//desturction code
}
};
int main(){
std::ostream log("log.txt"); //logfile as an example
setup();
myclass obj;
while(everything_is_fine()){
//main loop stuff
}
}
The problem is: I don't know what's the best way to terminate the program.
I don't want to catch an exception in main
. It would be kind of pointless and ugly, because the exception cannot be handled anyway. Even so, I'd like to have some sort of stack unwinding mechanism. If I simply exit
the program inside the if
blocks, then a log file for example wouldn't be destroyed properly. Am I right?
Would the file close if I throw inside the if
but without providing a try-catch block anywhere?
How to deal with exceptions occuring in constructors?
Is there a best way to handle this type of problem?
I hope it became clear what my problem is.
Thanks for your answers, Have a nice day or evening.
Upvotes: 0
Views: 104
Reputation: 36597
It depends, and you really haven't given enough information. Generally speaking, there is no absolute "best" - it depends on needs of your program, rather than there being a "one size fits all" approach.
It is only true in small trivial programs (e.g. the sort of things you'll do in class exercises, rather than a workplace) that an error always requires immediate program termination. The real-world need is fuzzier than that - depending on what your program does, there is often an option to recover from the error and continue (either normally, or in some degraded mode). It may also be preferable to take steps to prevent an error (e.g. detect bad data, and do something to recover before doing an operation on bad data and causing an error condition).
Generally, though, if an error occurs in a constructor (and it is unavoidable, and the constructor can't do anything to recover once it occurs, etc) it is necessary to throw an exception. This basically signals a need for the caller (or some function in the call stack) to take recovery action. If the caller is unable to recover, the default result of throwing an exception is program termination - after invoking destructors of all objects created locally (of automatic storage duration) in the call stack. This terminates the program - if that is necessary - and does cleanup in the process, as long as destructors clean up properly (which is the purpose of destructors).
In your code, throwing an exception will (eventually) return control to main()
. If the exception is not caught, the program will terminate - but not before log
is destroyed - which invokes its destructor. The destructor of standard output stream classes generally flush the stream and close it properly. If you need to do more than that (e.g. other recovery action before terminating, after flushing the stream) write main()
as a function-try-block.
It is usually inadvisable to do "partial construction" in a constructor - for example, a constructor setting up some basics, but the user then having to call another function to do "further" initialisation. Such techniques are an opportunity to forget to do the initialisation - which basically means subsequent code gets to use an object that is not initialised properly. In C++, such techniques are rarely necessary anyway - it is possible to hold off on creating an object until all information is available to properly initialise it (in the constructor).
Generally speaking, returning error codes (e.g. a function with a non-void
return type, a function that accepts a pointer/reference to an object which stores status information) is appropriate in different circumstances. There is nothing that forces a caller to check the return value from a function. So a return code is appropriate if an error condition can be safely ignored (e.g. if your code forgets to check it) or if the function is only used in a circumstance where the return code will be checked. There is nothing preventing you writing code that converts a return code (say, from a function written in C) into an exception. The problem with return codes is that it is possible to forget to check them - which can mean critical errors remain undetected/unreported, and cause faults in other code within the program.
Upvotes: 1
Reputation: 317
One solution is not to write your constructors in such a way that they would throw errors. A common practice is to use constructors to set up member variables, etc., then have a method such as bool initialize(); that returns a value based on whether or not the class can do the more complicated initialization without error.
Instead of a bool you can likewise return other values or structures for more informative errors.
In the end, your log file can still be written to by any class and should contain information on the errors, if you get any.
Upvotes: 0