Sign in to follow this  
Giblodian

C++ Zombie flag altneratives

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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( "..." );

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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");

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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 throws
cout << 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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Quote:
Original post by Giblodian
or the method I originally mentioned just seem much simpler than fiddling around with throw,try,catch everywhere.


If you're fiddling around with them, you're probably not using them the way they were "supposed" to be used. throw statements are a replacement for "return ERROR_CODE;" in many situations (though often error codes are still suitable in other places). But try/catch()es should be far less frequent than checking for errors. All in all, it should be significantly less fiddly.

Your example would also have the client fiddle around with manual resource management, too. Not to mention the fact that it could potentially throw a bad_alloc exception (and that does happen on Windows if you're creating a particularly hungry app).

I'm not saying exceptions are a silver bullet. Sometimes, they are a royal PITA. But I would suggest getting to know them better. In some ways (namely stack unwinding), C++ actually has better machinery to handle exceptions than many other languages.

Share this post


Link to post
Share on other sites
Quote:
Original post by Giblodian

I simply do not like exceptions.

Then just do the following:
bool foo(char * filename) {
FILE * f;
f= fopen (filename,"r");
if (f != NULL)
{
processContents(f);
fclose (f);
}
}
It's C (possibly with classes), but it works well. It's usually better to stick with POD types, saves a lot of headaches.


Exception-less RAII is possible, just requires discipline. All classes look like this, init() might be virtual:
class Foo {
Foo() {} // empty
bool init(); // actual constructor
};
and is used simply as:
Foo foo;
if (!foo.init()) return; // or panic in other ways
// use foo from here on

The rest works as one would expect. The only difference is that you must make it a habit to do construction in two lines, handling the init()'s result properly. You still get auto clean-up, but lose some of the benefits of true RAII.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zipster
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");
My opinion is that a file object should represent a file, it can open, close, create and delete that file as often as needed. So it is also perfectly valid (and un-exceptional) if the file cannot be opened right away. You shouldn't be able to ask it to open a different file though, that doesn't make sense, there shouldn't even be a mechanism for that (i.e. A filename is taken upon construction and open simply reopens that file for reading and/or writing). IMO such an object is largely useless, you'd never need to create instances of file objects, the only state they keep is the filename and after that they're just manipulating the filesystem using that name.

A file-stream, on the other hand, is a different creature entirely. A single stream can be open or closed, when the stream is open it is directed at a single file source/sink. As a consequence of it not modelling a file, but a stream, it can be made to point to any number of files during its lifetime. If a file won't open then the stream simply remains closed which, as already established, is a common and valid state for it to be in, certainly not an exceptinal one.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zipster
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.
It's possible to argue about the meaning of "exceptional" all day. The historically terrible efficiency of throwing exceptions has led to much quibbling about what is or is not exceptional in C++, as opposed to Java where exceptions are used anywhere and everywhere they come in handy. The fact that no other mechanism exists to indicate a failed constructor in C++, however, suggests that the existence of an object utterly unable to fulfill the postconditions of any of its functions is exceptional regardless of how likely it was to happen. The creators of the IOStreams library decided to go a different route and incorporate the state of the stream into those postconditions, significantly complicating the interface but increasing its flexibility. Whether that was the right route to go is debatable, but the fact is that it was a design choice, not the one true approach. If you want to indicate an error, and you think of it as an actual error rather than just an alternative state of being, throw an exception.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sneftel
The fact that no other mechanism exists to indicate a failed constructor in C++, however, suggests that the existence of an object utterly unable to fulfill the postconditions of any of its functions is exceptional regardless of how likely it was to happen.

I don't disagree. But you're assuming that a) if you failed to open the file in the constructor, the file object itself failed to be constructed, and b) that the the post-conditions of all your functions always assume success and throw exceptions otherwise. As you said yourself these are just design decisions. If I instead defined my post-conditions to include an error state, then I could argue it isn't exceptional for things to fail and the constructor can just set a flag without throwing an exception. The file object is successfully constructed but you have to deal with the error before continuing to use the object.

It all comes down to preference. As you put it, I think a file class should have "alternate states of being" ala fstreams, but that's just me.

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
My opinion is that a file object should represent a file, it can open, close, create and delete that file as often as needed. So it is also perfectly valid (and un-exceptional) if the file cannot be opened right away. You shouldn't be able to ask it to open a different file though, that doesn't make sense, there shouldn't even be a mechanism for that (i.e. A filename is taken upon construction and open simply reopens that file for reading and/or writing). IMO such an object is largely useless, you'd never need to create instances of file objects, the only state they keep is the filename and after that they're just manipulating the filesystem using that name.

Why doesn't it make sense? Why is it strictly necessary for a file object to be bound exclusively to a single file[name] for its entirely lifetime? Why is such an object largely useless otherwise?

I think of a file object as something that can be "bound" and "unbound" to different files as often as is wanted, much like you see streams, and not necessarily as an object that actually represents the file itself. Admittedly I rarely use such functionality, but it just seems odd to me that such a mechanism would suddenly cast an otherwise decent file object into the realm of the outrageous and nonsensical.

Share this post


Link to post
Share on other sites
I feel I have failed to accurately describe my use. This is what I'm doing.

/*DON'T MOCK ME*/
//Generalized Image class
class Surface
{
public:
//Load image from disk. Set all the private variables, etc.
Surface(const char *file = "image.bmp");
~Surface();

//is called at every method to make sure it isn't a zombie
bool isNull() const;

int getWidth() const;
int getHeight() const;
const SDL_PixelFormat* getFormat() const;
int getAt(int x, int y);
bool setAt(int x, int y, RGBA& rgb);
protected:
void init();
void printNotSet() const;
private:
SDL_Surface *Bitmap;
int Width, Height;
bool Null;
};
/*then I have this, which extends the general image class to include operations proprietary to the editor. It is *completely* dependent on an initialized Surface. Note:
it's incomplete and planned to do more in the future*/

class MapSurface : public Surface
{
public:
MapSurface(const char *name);
~MapSurface();


//calls isolateObject->traceObject->simplifyObject
void createObject(int x, int y);
private:
void isolateObject(int x, int y, int oldColor, int newColor);
void traceObject(int xStart, int yStart);
void simplifyObject(double tolerance);

int **_mapPixels;
};



So pretending you're me, only not retarded, what would you do? Exceptions seem the rave and my weird factory thing doesn't appear well received (or even appear to make sense now) so I'm just going to accept that.

My questions are:
Where would you load the file: from the parent or derived class?
from the constructor or prime candidate for an open method?
If loaded from the parent constructor, and an exception is thrown when an error occurs, how would this be utilized/caught? I'm keen on not aborting or exiting, because this is UI driven and it'd be nice to have the opportunity to try opening something else.

is structuring it like this wrong entirely?

Share this post


Link to post
Share on other sites
Quote:
Original post by Giblodian

is structuring it like this wrong entirely?


No, just clumsy.

Look at the example I gave above, which behaves just like exception-based approach would:
class Foo {
Foo() {}
virtual ~Foo();
virtual bool init() { return true; };
};


Then, since you cannot rely on exceptions to check construction, you need to do it yourself each time you use an object:
int main() {
Foo foo;
if (!foo.init()) return 1; // foo is broken, we can't continue at all
foo.whatever(); // foo is fine
};


For inheritance, there are no problems:
class Baz : public Foo {
Baz() : Foo() {}

virtual bool init() {
if (!Foo::init()) return false;
// derived init
}
};


The exceptions equivalent of this would be:
class Foo {
Foo() {
if (!init()) throw exception;
}
};


Since constructors work in a different way and have no error reporting abilities, they need to be ignored completely. Whatever the constructor should be doing goes into init(...).

This is sometimes useful anyway, since, unlike constructors, it allows existing instance to be reset. Some smart pointers use such approach, as does auto_ptr.

Share this post


Link to post
Share on other sites
Quote:
Original post by Giblodian
I feel I have failed to accurately describe my use. This is what I'm doing.
*** Source Snippet Removed ***

Your surface class is read-only and includes no non-constructor facilities for loading in data. Given that, there is absolutely nothing meaningful or useful to do with a Surface whose constructor has failed. I think everyone here would agree that that's an exception situation, regardless of what they like their File class to do in error conditions.

Quote:
Where would you load the file: from the parent or derived class?
As you've structured it, the parent; after all, the parent needs to still be a Surface, so it still needs to do things that Surface does. MapSurface should expect that of Surface.
Quote:

If loaded from the parent constructor, and an exception is thrown when an error occurs, how would this be utilized/caught?

Wherever you like, further up the call chain. If you have a function that handles the "File->Open..." menu option by putting up a dialog box and then opening the resultant filename, that would be an ideal place to throw up a message box and then return if the constructor throws.

Share this post


Link to post
Share on other sites
Quote:
Original post by Zipster
Why doesn't it make sense? Why is it strictly necessary for a file object to be bound exclusively to a single file[name] for its entirely lifetime?
I only meant that a file class designed to model a file on the filesystem shouldn't be able to bind to different files during its lifetime. Any operations that can be performed on a file instance should mimic operations that you can perform on a file and you can't ask a file to become another file.
For sensible pragmatic reasons it is probably useful to modify the semantics to allow an instance to bind to different files, then that class would model (in my own off-the-cuff parlance) a file-handle.

Quote:
Why is such an object largely useless otherwise?
By object I mean an instance of such a class - the same functionality can be just as well be achieved via static/free functions - although I might be wrong about this as I can imagine you might want to keep other state too and the Java File is used via an instance afterall, it does demonstrate the immutability of the filename idea though. Just that in a language where file-streams already exist it seems that the other functionality that they don't provide wouldn't require instances of a file class but could be implemented as static methods of such a class for example.

Quote:
I think of a file object as something that can be "bound" and "unbound" to different files as often as is wanted, much like you see streams, and not necessarily as an object that actually represents the file itself.
Yeah, that would be the difference between our notions: a file object that represents a file and a file object that represent a file handle.
(Although I'm not sure I would allow a file handle to be in an unbound state, that adds a lot of complexity to the semantics, but I would allow it to be rebound. I'd do the same for streams if I were to implement my own).

Quote:
outrageous and nonsensical.
Haha, perhaps I came across a little strong [grin]

Share this post


Link to post
Share on other sites
Quote:
Original post by dmatter
(Although I'm not sure I would allow a file handle to be in an unbound state, that adds a lot of complexity to the semantics, but I would allow it to be rebound. I'd do the same for streams if I were to implement my own).

By unbound I meant "not currently open". To take the HANDLE type as an example, if you initialize it to INVALID_FILE_HANDLE it's effectively in an unbound state, and if you open a file and then close the file, the handle is once again unbound. Plus you'd need to be in the unbound state before you can bind to something else. But I agree, I think our difference in mindset comes from the files versus file handles.

Share this post


Link to post
Share on other sites
A good thing about using exceptions is that you can handle all different kinds of failure to load at one single place. I find myself liking the java io exception model for files/streams.

Example how I think it should look like:


auto_ptr<Surface> image;
try {
IFileStream file("image.png"); // may throw IOException if file not found
image = image::png::load(file); // may throw IOExcetion unless valid png file
verify_image_format(image);
} catch(const IOException & e) {
// warn and print exception e
image.release(); // Release any allocated image
}

// Not loaded, try another image
if (image.get() == 0) {
IFileStream file("image2.png"); // may throw IOException if file not found
image = image::png::load(file); // may throw IOExcetion unless valid png file
// No backup plan, cannot live without loaded image, let exception trickle
// through
}


Share this post


Link to post
Share on other sites
What I've done in this case is had the InputFile class support a cast to operator bool() (or a bool isOpen() const), returning true if it got the file and false otherwise. And then I have a nice comment telling the user that after constructing the object, they are responsible for testing it for truth. If untrue, they are required to deconstruct it, not use it. And then my read() and write() functions are free to neglect to do checking - if the user breaks the rules, he'll trigger some variation of a NULL pointer crash, debug, find the comment, perhaps curse my name - but end up with working code.

It sounds draconian, but in practice a lot of code looks like this:


bool parse(const char* filename)
{
InputFile file(filename);
if (!file) //got it?
return false; //I guess we aren't doing any parsing here
file.lots of work...
return true;
}


which means checking for existence at the start of the function is not much of an imposition, since they wouldn't be continuing anyway, and saves your code from checking. the. same. flag. every. single. time. and. always. finding. it. true, as they merrily parse tokens.

This approach has the advantage of not forcing exceptions into code that doesn't want them, while allowing someone to wrap your class in code that does check and throw, if they want that. Sometimes it's kind to defer design decisions like that.

A class doesn't have to deal with any arbitrary, nonsensical operation anyone cares to dump on it. It just has to state a contract with the user, promising what works, what doesn't, and suggesting what happens when the contract is broken. Requiring a check for success is a reasonable contract.

Share this post


Link to post
Share on other sites
Correct me if I'm wrong, but I always thought exceptions would not roll back all the calls you did since the try start.
So if you did something like:

try {
MyData *dataObj = new MyData;
...
MyFile configFile("controls.cfg");

} catch( myexception) {
printf("meh, couldn't do it.\n");
}

Then the allocated MyData would still be there, and you had no way to clean it up.
I also thought, that using exceptions is taking quite a bit longer to execute than without.
I use exceptions for stuff that I simply can not handle, and which causes my program to shut down, or go into a pre-shutdown state(allowing to retrieve some information before quitting).
I guess if it really rolls back all the changes done since the try {, then you could use it like you say, but there would still be the performance issue, wouldnt it ?
( it would have to roll back all the changes you did, I'd imagine that could be significantly slower depending on how much you did since the try start.

Share this post


Link to post
Share on other sites
If you are using exceptions, you probably aren't doing
Thing* t = new Thing();
because, you're right, you are begging for leaks. What you are probably doing is some variation of
SmartPointer<Thing> t = new Thing();
where SmartPointer<Thing> has a destructor that frees what it points to. Don't think of using exceptions unless you avoid new entirely, or know what smart pointers do. It only leads to grief.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this