C++ Zombie flag altneratives

Started by
34 comments, last by SiCrane 14 years, 8 months ago
What are some eloquent ways to avoid fun stuff like segfaulting on objects that depend and operate on dynamically loaded data? A file class for example. Example (pseudo code):

class FileClass
public:
    File(const char *name)
    void write() 
    void read()
    void whateverone()
    void whatevertwo()
usually I have a bool indicating whether the constructor loaded the file correctly or not, then I check it in each of the methods but this is a PITA and sometimes I have to move the error checking down into derived classes. I could throw an exception, but it'd only be useful if I threw in an exit(1) -- and that's something I really hate doing especially with libraries -- otherwise I'd still have to use the zombie flag. Or am I wrong? I've thought about doing something like this

class FileClass
public:
    FileClass(FILE *)
    void write() 
    void read()
    void whateverone()
    void whatevertwo()
private:
    FileClass() 

FILE *file = fopen("nonexistant", "r+"); 
if (f != NULL) 
    FileClass o(file) 
loading and verifying the data outside the class, passing only something that's known to be valid. That way there are no worries of zombies. Are there any pitfalls with this approach? Any patterns worth looking at? What do you use? Note: FileClass is just a generic example.
Advertisement
If you don't/can't use exceptions then one useful trick can be to wrap up the creation in a factory function, which will check that data was read successfully:

FileClass* open(const char* path){  FileClass* f = new FileClass(); // Does minimal work, nothing that can fail  const bool initOk = f.Init(path); // Does work which may fail  if (initOk)    return f;  else    delete f;}

This gives you the opportunity to do work which may fail (Init()) in a construction-like way without using exceptions.

Exception-safe error handling and smart pointer use left as an exercise for the reader. Bonus points for making the c'tor and Init() private so this is the only way to create a new FileClass object.
Quote:Original post by Giblodian
I could throw an exception, but it'd only be useful if I threw in an exit(1)

You can't throw an exit(1). What do you mean exactly?
EDIT: sorry, I misread what you wrote.

Throwing an exception is often a valid option. You may have reasons not to, but can you elaborate on those?
Yeah, I'd do the same as OrangyTang.
If the class's function depends on if the constructor fails, then it's best to have an init function for it, since you can't use it anyway if it failed you have to get rid of it or if it wasn't dynamicly allocated, exit the function using it.
For dynamic allocations, I usually create a function like OrangyTang wrote but as a static one inside the class.. like FileClass::OpenFile( "..." );
You should really only throw an exception if your code runs into a situation it can't or shouldn't handle. A file class trying to open a file that doesn't exist isn't exceptional behavior, since that's just the nature of file classes and other objects which bind themselves to external resources which may or may not exist, or have other permissions assigned to them preventing you from performing certain actions. While you may want to open a particular file, the object should be able to gracefully deal with the case where the file doesn't exist by returning an error or entering a fail state.
Quote:Original post by Zipster
A file class trying to open a file that doesn't exist isn't exceptional behavior, since that's just the nature of file classes and other objects which bind themselves to external resources which may or may not exist.


Actually, I would say that it is exceptional behaviour. Most of the time I'd expect to get a valid path to an "openable" file. There's no point in creating a useless file object. It would be even more exceptional in my mind if the file couldn't be opened for writing.

I guess it's a matter of taste. std::ifstream clearly agrees with you.
Quote:Original post by the_edd
Actually, I would say that it is exceptional behaviour. Most of the time I'd expect to get a valid path to an "openable" file. There's no point in creating a useless file object. It would be even more exceptional in my mind if the file couldn't be opened for writing.

As the user of the file class, I would also expect the file to be opened or created. If perhaps this was a critical resource like the model or texture package then I might throw an exception if the file couldn't be opened. But as far as the file class itself is concerned, this sort of thing happens all the time. It isn't outside the realm of what the file class can reasonably deal with, which is what I meant by "exceptional". It's isn't an uhoh-something-went-wrong-and-my-world-is-collapsing-around-me situation :)

Plus the file class isn't necessarily useless at that point. The object itself should remain in a perfectly valid state so that you can do something like this:
FileClass file("this_file_doesn't_exist.txt");if(!file.is_open())   file.open("this_file_does_exist.txt");
Personally I'd go with unopenable files being exceptional cases and have some other mechanisms to check if a file exists/is accessible. A file object shouldn't have to deal with existing in a world where the file it's supposed to represent doesn't exist/can't be accessed; the client should query some other object like a File System or Directory or a static method in FileClass or what have you before attempting to open a file.

That way if you as the client are in a situation where you know the file should exist/be accessible and have no backup plans if it's not you get an exception if that happens, and if you know it may not exist you query for it first and go to plan B if you must.
Quote:Original post by Zipster
As the user of the file class, I would also expect the file to be opened or created.

Well, I'd say it depends on how the class is used.

If I put up a GUI with an "open file" dialog such that it was impossible to provide a non-existent file, failing to open that file would be exceptional.

Quote:If perhaps this was a critical resource like the model or texture package then I might throw an exception if the file couldn't be opened.

What difference does it make if the resource is critical or not?

Quote:
But as far as the file class itself is concerned, this sort of thing happens all the time.

If you mean that it's certainly a case that should be handled, I completely agree.

Quote:It isn't outside the realm of what the file class can reasonably deal with, which is what I meant by "exceptional". It's isn't an uhoh-something-went-wrong-and-my-world-is-collapsing-around-me situation :)

Again I agree, but I don't think an exception implies that "uhoh" situation. I guess your mindset is just different.

Quote:Plus the file class isn't necessarily useless at that point. The object itself should remain in a perfectly valid state so that you can do something like this:
FileClass file("this_file_doesn't_exist.txt");if(!file.is_open())   file.open("this_file_does_exist.txt");


You could do the same with a try/catch block. I don't get what you mean here.

But I think we have different ideas about the purpose of the class. I guess you're thinking of it as something which is responsible for looking at both a file's data and also querying its file-system attributes, where as I'm looking at it as something that's merely for reading the data.

If I wanted something to do both, I'd probably do something like:

fs_entry somefile("somefile.txt"); // never throwscout << somefile.exists() << endl;data_stream &ds = somefile.open(); // may throw


All this does beg the question "why not use std::ifstream?", though.


Regardless of the merits of each approach, I still don't get why throwing an exception isn't an *option* for the O.P.
First, I'm just a hobbyist, so no need to worry about me contaminating the code pool or whatever.

I simply do not like exceptions. Always seems to turn into a mess when I use them. Something like this
class FileClass{public:	static FileClass* create(const char *name) 	{		FILE *f = fopen(name, "r+"); 		if(f != NULL)		{			FileClass *r = new FileClass();			r->file = f; 			return r; 		}		else return NULL;	}	~FileClass(); private:	FileClass() {}	FILE *file; };int main() {	FileClass *o = FileClass::create("doesntexist"); 	if(o == NULL) { 	//whatever	}}

or the method I originally mentioned just seem much simpler than fiddling around with throw,try,catch everywhere. May not be correct thought, but it does seem to give the appearance of working.

This topic is closed to new replies.

Advertisement