Structured Exception Handling Basics, contrasting SEH with standard C++ exception handling." />
Windows' SEH and C++ Exception Handling
IntroductionI recently read Vadim Kokielov's article on Windows' SEH, but I think that there were some unfair, and perhaps hasty, conclusions reached in the comparison of Windows' SEH with C++ EH. My article is intended to be a rebuttal and is provided merely for additional insight into the topic of C++ EH. It is not intended to be completely unbiased. If anyone can correct me on these arguments and suggestions (particularly Vadim) I would appreciate it and post the conclusions as updates here, or perhaps as a part 2 of this article, if the GDnet staff is willing to format it (Ed.- we are). Please do not post a reply intended for me on the GDNet message boards. Any email I get containing any of the following: violent replies, wild assumptions, and any arguments related to C vs. C++ or OOP vs. Structured Programming will be deallocated immediately :) and no credit will be given to the author of such crap in the event that he/she manages to provide useful information. Onward to the rebuttal. :) Termination HandlersIn comparison with C++ exception-handling, termination handlers really only matter for resources that will not be deallocated by destructors (i.e., temporary resources dynamically allocated by non-C++ code from inside a function, or from inside a constructor). In all other instances, a properly-written destructor will be able to clean it up. For such special instances, one can easily write a class template that will take advantage of the destructor calls made when local variables go out of scope. It only has to be written once (per programmer lifetime :), plus a simple deallocator (in most cases, a single function call). I would remind you that although the deallocator has to be written (once per type, not per instance) and the template class instantiated in C++ EH, in SEH you would have to write the deallocation function calls in your __finally block anyway. ExamplesThe class template need only be a slightly modified version of std::auto_ptr. For example, namespace auto { namespace deallocation { template <typename T> struct heap { void operator () (T* const pointer) { delete pointer; } }; struct handle { void operator () (HANDLE const handle) { CloseHandle(handle); } }; struct bitmap { void operator () (HBITMAP const handle) { DeleteObject(handle); } }; struct window { void operator () (HWND const handle) { DestroyWindow(handle); } }; struct file { void operator () (FILE* const handle) { _fclose(handle); } }; struct com_object { void operator () (IUnknown* const handle) { handle->Release(); } }; // etc. }; template <typename T, typename D = deallocation::heap<T> > class handle { T* pointer; D deallocate; public: handle(T* const pointer) : pointer(pointer) {} ~handle() { if( pointer ) deallocate(pointer); } // ... }; }; Then you simply use it similarly to std::auto_ptr, substituting any custom deallocators when the need arises: void myclass::myclass(const char* const filename) { auto::handle<some_big_class> bc = new some_big_class; auto::handle<FILE, auto::deallocation::file> fp = _fopen(filename, "r"); // bc and fp are automatically closed whether or not an exception is thrown } Personally, I think that std::auto_ptr should have been written to support custom deallocators. Note that a partial specialization of auto::handle should be written to include Windows' handle types and all other types that require the first template argument be a pointer type. ConclusionThis solution has the following benefits over SEH:
Performance and FlexibilityException FiltersC++ directly supports exception filters, in the form of type-checking. Consider the following example. Examples// These "empty" types allow us to filter exceptions - typically defined elsewhere struct exception {}; // anything struct file_exception : public exception {}; // all file exceptions struct eof : public file_exception {}; struct file_not_found : public file_exception {}; struct unsupported_seek : public file_exception {}; struct out_of_memory : public exception {}; // Function declarations for example void do_something() throw( eof, file_not_found, unsupported_seek ); void do_something_else() throw( out_of_memory ); void some_complex_function() { try { do_something(); do_something_else(); } catch( eof ) {} // catches end of file exceptions catch( file_not_found ) {} // catches file not found exceptions catch( file_exception ) {} // catches all other file exceptions catch( exception ) {} // catches anything else } C++ EH's type-safety also has a notable advantage over SEH. C++ EH can use any type for filtering, while SEH is restricted to packing values into an unsigned int in order to emulate a more primitive form of type-checking. Why is this important? Well, duplicate types are not allowed by the compiler in C++ EH, but since SEH uses only an unsigned int value, its value might conflict with exceptions defined by other code. C++ EH protects C++ from incorrectly handling code written by other people, since the unknown exception will simply keep unwinding the stack until it is either handled by someone who actually _knows_ the type, or the program ends. With SEH, if two exception constants have the same value, it is possible for client code to throw an exception and your code to misinterpret it. Chances are that 100 years down the road, your grandchildren's X-Plus code will catch and interpret the exception differently than was intended. ;) PerformanceAlthough C++ EH is supposed to be slower than SEH, I do not understand why that matters in this comparison. EH is used to handle exceptional conditions, and is not intended to be used as a replacement for assertions, logging, or return values in time-critical code. SimplicityCuriosly enough, this category was not introduced in the comparison. C++ EH requires 3 new keywords, while SEH requires that plus learning a new (small) API. Any additional conditional logic needed to handle the exception (or simply rethrow it) can and should be written _inside_ the appropriate catch block. The C++ EH conditional logic is exactly the same as normal (i.e., if...else, etc.), while SEH depends on API functions and specialized code, and precludes the use of normal "flow of execution modifiers" unless you want to lose the performance "advantage." PortabilityC++ EH is portable across all platforms that support the C++ standard. SEH is portable across all languages that Windows supports and compiler-writers comply with. I prefer the former. Conclusion to the RebuttalIf you use C++, then C++ EH is probably the better choice. Of course, that is coming from a C++ user, so it is immediately invalidated. ;) (j/k) - null_pointer (null_pointer_us@yahoo.com) Discuss this article in the forums
See Also: © 1999-2011 Gamedev.net. All rights reserved. Terms of Use Privacy Policy
|