Поделиться через


STL functors, scoped handles and how we can use them in Windows programming

Firstly, it's election day so get out there and vote! Secondly, I went to bed last night and dreamt about functors! Last Friday I code-reviewed some changes made by one of my colleagues and his code just happened to use some STL containers and all sorts of related goodies. The last time I tried to use STL was about eight years ago when I was still at college and, admittedly, I didn't use them much: compiler support (both on Unix and Windows platforms) was not great and so I ended up using Matlab for most of my work instead.

Anyway, this got me thinking: wouldn't it be nice to be able to use the STL auto_ptr template to manage Windows handles in addition to regular old object pointers? Unfortunately, this was a no-go for two main reasons:

  1. The semantics are all wrong: auto_ptr<HANDLE>, where HANDLE is a generic Windows-style opaque pointer, doesn't work as the resulting auto_ptr specialization is semantically equivalent to HANDLE * (i.e. a pointer to a HANDLE). This is one level of pointer indirection too much to be useful for us.
  2. auto_ptr, unlike the TR1 shared_ptr, does not allow the developer to define a custom deleter function: auto_ptr always invokes the delete operator on the type of the encapsulated pointer. Windows handles typically use CloseHandle or something similar.

So, I began to write my own. First, I thought I'd steal some inspiration from the auto_ptr and shared_ptr classes. Unfortunately, this is not easy as it might sound. Anybody who has ever read the source code for STL would likely agree with me on this: the STL headers are not designed to be humanly readable as far as I can tell. Therefore I had to write it from scratch and reproduced below is my scoped_handle implementation:

Click to view source code for scoped_handle

Of course, I went through several iterations before I got it right. The first used a single encapsulated deleter function pointer. This suffered from the main issue that the deleter function had to conform to a very specific function prototype. Thus it becomes difficult to produce a generic class that can handle void (*)(HANDLE) functions and BOOL (*)(HANDLE) equally well (CloseHandle belonging to the latter category). After some head scratching I developed the implementation listed above based on the STL-style concept of functors. Specifically, this template class can handle any object, be it a function or class or anything else, as long as it supplies an overload of operator() that takes a single appropriately typed argument. This operator can return any type since scoped_handler ignores the return value anyway.

A few caveats become obvious:

  1. Beware compiler warning C4930 from Visual C++! This warning is the reason why I did not provide a constructor of the form explicit scoped_handle(CALLABLE_TYPE) which would have created a default, "empty" handle set to UNDEFINED_VALUE initialized with the specified deleter. The compiler cannot distinguish between a class instantation or function prototype in this type of code.
  2. This code throws C++-style exceptions. If you're using this class in COM code you must catch all C++ exceptions and translate them into equivalent HRESULTs or stack-unwinding chaos will ensue.

This is roughly how one might use the scoped_handle class in conjunction with common Windows APIs:

Click to view source code for example of usage

See you next time!

ScopedHandle.zip

Comments