Starting from the smart_ptr headers (shared, scoped, and weak), I've created corresponding handle classes. The motivation is to provide RAII semantics for handle based resources. Windows' HANDLEs, FILE*s, and file descriptors are the most obvious examples.Being primarily a Unix developer, I think this library could have a great use in Unix as long as it can do a few things. Namely, it needs to be flexible enough to deal with C style inherent (i.e. it should allow for custom features for socket type but I should also be able to use file descriptor features for that same type since a socket is a file descriptor).
I think a policy based design would be interesting to pursue here.
(2) is designed to deal with the fact that the actual type of a handle rarely uniquely identifies the handle type. Most Windows' handles are actually void pointers, file descriptors are ints, etc. Because the smart_handle classes take a handle policy as a template parameter, smart_handles that are logically distinct types will be properly distinguished by the language's type system. For example, to define a scoped_handle for a Windows' file HANDLE you'd do the following:Instead of having to declare a policy, why not just exploit the fact that names are unique using a different namespace and some preprocessor macros? I've included an example below.
But the above would be valid for HBITMAPs since what you're declare is really only specific to HANDLEs. an win_bitmap_handle traits class would be identical to the above wouldn't? So should I just typedef win_file_handle win_bitmap_handle? Then we're back to square 1.struct win_file_handle { typedef HANDLE handle_type; static bool is_valid(handle_type h) { return h != INVALID_HANDLE_TYPE; } static void release(handle_type h) { if (is_valid(h)) ::CloseHandle(h); } static handle_type default_value() { return INVALID_HANDLE_TYPE; } static bool equal(handle_type lhs, handle_type rhs) { return lhs==rhs; } }; typedef scoped_handle<win_file_handle> scoped_win_file_handle;
This type would be unrelated to, for example, a Windows HBITMAP, although both HANDLE and HBITMAP are void*s.
- Semantically, handles simply aren't pointers. E.g, the pointer deference operators make no sense for handles.But if you think of think of a smart handle as a container containing a C type, then it makes a little more sense.
- Otherwise incompatible handles may in fact be typedef'd to the same thing - smart_handle makes these different types, smart_ptr does not.The biggest problem is that for win_file_handle for example, it's probably almost as easy to just implement a wrapper class that provides a constant interface than to use smart_handles.
I've uploaded the implementation to the Yahoo group's file area as smart_handle_jm1.zip. I've included a rudimentary test case for smart_handles whose main goal is just to exercise all of the code.I like the initial implementation. I've included a small sample of a similiar class as a proof-of-concept of adapting smart_handle to be a little more flexible and easier to use. The main differences are:
- A policy based design to whereas the policy extends the classes interface.
- Stronger typing (the types are different based on typename, not on traits). The traits approach seems fundamentally flawed as two separate types could share the same traits.
This was all just a quick hack. I just discovered this thread this morning so feel free to ignore all of it :-)
Regards, Anthony Liguori
/*\ * safe_wrapper.cpp (C) 2003 Anthony Liguori <[EMAIL PROTECTED]> * A C "handle" to C++ safe type class \*/
/* These headers are only required for out stub */ #include <iostream> #include <unistd.h> /* macros to access and declare our C++ safe types I added support for inherentence to encourage the expression of inherentence relationships in C (for instance, HWND -> HANDLE or socket -> fd). */ #define wrap_t(type) boost::wrapper::type #define decl_base_wrapper(type) namespace boost { \ namespace wrapper { \ struct type { typedef ::type self; \ ::type value; }; } } #define decl_wrapper(base, type) namespace boost { \ namespace wrapper { \ struct type : public base \ { typedef ::type self; }; } } /* we define some C-style types */ typedef void *HANDLE; typedef HANDLE HWND; typedef int fd_t; typedef int socket_t; /* our default policy */ template <typename T> struct empty_policy { empty_policy(typename T::self &value) { } }; /* a sample scoped fd policy */ struct scoped_fd_policy { scoped_fd_policy(fd_t &value) : value(value) { value = -1; } ~scoped_fd_policy() { if (-1 != value) { std::cout << "::close(value)" << std::endl; } } fd_t &value; }; /* our main type wrapper class */ template <typename T, typename Base=empty_policy<T> > struct type_wrapper : public Base { typedef typename T::self Type; type_wrapper() : Base(value) { } type_wrapper(Type value) : value(value), Base(value) { } Type &operator*() { return value; } Type &operator->() { return value; } private: Type value; }; /* we declare those types to be C++ safe types */ decl_base_wrapper(HANDLE); decl_wrapper(HANDLE, HWND); decl_base_wrapper(fd_t); decl_wrapper(fd_t, socket_t); int main(void) { // just some type safe versions of the types... type_wrapper<wrap_t(HANDLE)> wrap; type_wrapper<wrap_t(HWND)> wrap1; type_wrapper<wrap_t(fd_t)> fd; // an example of using a scoped policy on a fd_t type. Note: this same // policy would work for a socket_t because of the inherentence. type_wrapper<wrap_t(fd_t), scoped_fd_policy> fd1; type_wrapper<wrap_t(fd_t), scoped_fd_policy> fd2; std::cout << "fd = " << *fd << std::endl; std::cout << "fd1 = " << *fd1 << std::endl; *fd1 = STDOUT_FILENO; }
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost