Original Post
Forking off the discussion: here
Quote:How about just having the mapping class destroy it in it's destructor? Like you would do if you were using RAII, which is what we've said we're doing? Usually, file mapping does use reference counting or OS mechanism to prevent the same file from being repeatedly mapped to multiple locations within one process space, but there wouldn't appear to be a need for that. If you need to have the mapping stick around longer, you keep around that object longer. The lifetime of the mapping is tied to the lifetime of the mapping class.
Original post by Jan Wassenberg Frankly I have little interest debating the use of exceptions, especially when you only partially read what is presented and then scoff at it without understanding. But hey, let's give it a shot. Quote:Since you clearly haven't read the comments, let this reply address what you proposed: In your C++ version you're conveniently glossing over the complexity of, for example, making sure the file map is destroyed after the view (because the view relies on the file map). Are you going to use reference counting? [edit: code tag removed]
Switching this to using RAII and exceptions would look something like this:
Quote:I disagree. The two versions would preform exactly the same. Let's assume for a moment that it's illegal behavior to release the file mapping hfm before unmapping the pointer pv, in this snippit (I program mostly on/in parallel with linux, so I don't know if this is true):
Actually I'm glad you brought this up, because it illustrates perfectly the point being made: it is much easier to fall flat on your ass with exception cleverness, as opposed to error codes (which may be bulky but get the job done and *are in plain sight*).
HANDLE hfm = CreateFileMapping(h, NULL, PAGE_READ, 0, 0, NULL);
void *pv = MapViewOfFile(hfm, FILE_MAP_READ, 0, 0, 0); Using the RAII version using stack variables, the objects will be destroyed in the opposite order of creation, which means it's pretty much (is?) impossible to accidentally screw up the order (although one could intentionally saboutauge the effort by the manual calling of a destructor...): file hfm = CreateFileMapping( h , .... );
mapping pv = MapViewOfFile( hfm , .... ); pv will allways be destroyed before hfm in this case. Assuming we wanted to hold onto the mapping for an indefinte amount of time, let's say we were passing it around via boost::shared_ptr: boost::shared_ptr< mapping > pv = new mapping( ... ); Making sure that it's dependancy never dies would be a simple matter of either ownership or another shared handle, as members of the mapping class - or as a wrapper around both, which is easy to implement. Quote:The only fully general solution is to allways have a GetLastError or equivilant. Case in point: atoi, which returns an integer, for which all values are valid, yet some inputs are invalid ("hello world" does not convert to integer). Assuming an error is ignored by a programmer, the program will continue on with perfectly valid results from an invalid setup.
Quote:Easy solution: either multiplex them (e.g. if 0 is returned, call GetLastError) or return data in an OUT parameter.
(ignoring the fact that I can't return an error code and an icon, another problem of error-code returns):
Quote:I have a hard time remembering (and correctly attributing) names. It runs in the family. My uncle once called me by his own name, by accident, influenced by how often my mother and grandmother called me by his name. I can't even remember the Govenator's real name right now.
Quote:If you don't know of Raymond Chen, that says more about you than him.
I laughed when I read this: "It's really hard to write good exception-based code since you have to check every single line of code (indeed, every sub-expression) and think about what exceptions it might raise and how your code will react to it. (In C++ it's not quite so bad because C++ exceptions are raised only at specific points during execution. In C#, exceptions can be raised at any time.)" The entire point of exceptions is that you don't have to check every single line of code's return, and deal with it by hand, as you would without.
Quote:More likely, the calculation will be discarded entirely. If you're refering to a longer state object such as an exception thrown during the insertion of a new object into, say, a linked list, programming in consideration of Exception Guarantees, one could make, for example, the strong guarantee that the list will remain unchanged, leaving your state right as it was before you started. One can also make the guarantee not to throw. It is the fault of your library if 2 + 2 can thrown an exception, not the fault of exceptions.
If you do and are still laughing, then you probably haven't understood what's been said. So let's have another look: with return codes, you have to make sure each function call is wrapped in a CHECK_ERR macro (discussed by Alexandrescu under another name) or similar. Compare this with exceptions, where analysis is practically impossible because even trivial code sequences have an astounding number of paths through them. In particular, construction of [temp] variables may fail, throw, and leave your calculation in limbo.
Quote:And how does directly doing it by hand, or via macro, save you from this task? For every point an exception could be thrown, there should be a corresponding error code to be checked.
So instead of just looking for function calls and seeing that there's error handling code (direct or via CHECK_ERR), you have to think through every part of your code.
Quote:Shown how?
Clearly RAII alone isn't enough to save you, as shown above.
Quote:That's what documentation is for. //This code makes a Strong Exception guarantee. //This code makes a Basic Exception guarantee. //TODO: Check that code makes a Nothrow guarantee (e.g. add throw() to the //function and recurse into the subfunctions until all potential throwing //points have been verified to not throw. The same considerations must be accounted for in an error based mechanism all the same. if ( file_log.add_entry( "..." ) != OK ) { //What is the state of file_log? Can I try again after I've deleted some //porn, or has half of my message been written, the other half lost, and //my file descriptor invalidated? All of this depends on the guarantees //add_entry makes - exception based or otherwise. }
And when you revisit code, who knows if anyone even gave a thought to error handling? After all, in exception-based code, that need not be apparent.
Quote:I won't disagree that exceptions are easy to abuse. Then again, just about everything is - operators for defining language grammar in C++ thanks to boost::spirit, PHP's DLL interfacing functions to create OpenGL programs, the list goes on.
Now where does that leave us? Exceptions are surely good in some contexts (e.g. checking if object construction went OK), but this is much more limited than their actual use (c.f. the disgrace that is Xerces' end_of_entity "exception").
Quote:Is that a list of ANDs, or a list of ORs, out of curiosity?
I like the exception use guide presented in one of the last mails in the current thread:
Quote:There are entire picturebooks on the ABCs, that dosn't mean they're not simple. Less silly would be pre-algebra books. If duly motivated, I could probably write an entire book on rootbeer. Just because there's entire books on a subject, does not mean that it's not necessarily simple. Besides, you've missed that I've taken the assumption that we allready KNOW how to do it (see newly added emphisis).
Quote:Looks like that's only true if using the Ostrich algorithm: stick_head_in_sand(). Unfortunately it really isn't that simple, or there wouldn't be entire books on the topic ;p
It's very easy to write good exception-based code once you know how to.