Jump to content

  • Log In with Google      Sign In   
  • Create Account

Like
0Likes
Dislike

Windows' SEH and C++ Exception Handling

By null_pointer | Published Jan 15 2001 03:43 PM in General Programming

c++ seh exception struct void code const class written
If you find this article contains errors or problems rendering it unreadable (missing images or files, mangled code, improper text formatting, etc) please contact the editor so corrections can be made. Thank you for helping us improve this resource

Introduction

I 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 Handlers

In 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.

Examples

The 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.

Conclusion

This solution has the following benefits over SEH:
  • Class destructors become analogous to the __finally blocks.
  • Deallocation code (including its logic) only has to be written once per type, not per instance (as with SEH).
  • The habit of declaring resources as having automatic deallocation is easier to obey than declaring them normally and then remembering to clean them up.
  • The handle class is extensible in any way a normal C++ class is, while SEH must remain tied to structured programming (hence its name).
  • C++ EH and auto::handle are portable across platforms; SEH is not.
Performance and Flexibility

Exception Filters

C++ 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 grand children's X-Plus code will catch and interpret the exception differently than was intended. ;)

Performance

Although 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.

Simplicity

Curiously 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."

Portability

C++ 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 Rebuttal


If 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)





Comments

Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS