Adding functionality to a handle wrapper
I have a C++ RAII class for managing Win32 HANDLEs using boost::shared_ptr<> that looks a bit like this:
namespace detail {
struct NoDelete { void operator()( void* ) {}; };
}; // namespace detail
template< typename HANDLE_TYPE, typename HANDLE_DELETER >
class CHandleT
{
public :
explicit CHandleT( HANDLE_TYPE handle, bool delete_on_release = true )
{
if( delete_on_release )
开发者_开发百科 handle_ = Handle( handle, HANDLE_DELETER() );
else
handle_ = Handle( handle, detail::NoDelete() );
};
operator HANDLE_TYPE() const { return static_cast< HANDLE_TYPE >( handle_.get() ); };
protected:
typedef boost::shared_ptr< void > Handle;
Handle handle_;
}; // class CHandleT
struct DeallocateHandle
{
void operator()( void* handle ) { ::CloseHandle( handle ); };
};
typedef CHandleT< HANDLE, DeallocateHandle > CHandle;
I would like to extend it such that instead writing:
CHandle my_handle( ::CreateEvent( NULL, FALSE, FALSE, NULL ) );
::SetEvent( my_handle.get() );
I could write:
CEvent my_event( NULL, FALSE, FALSE, NULL );
my_event.SetEvent();
Would the best way to do that be to use the CHandle class as a member of a CEvent class?
class CEvent
{
public:
explicit CEvent( LPSECURITY_ATTRIBUTES lpEventAttributes = NULL,
BOOL bManualReset = TRUE,
BOOL bInitialState = FALSE,
LPCTSTR lpName = NULL,
bool delete_on_release = true ) :
handle_( new CHandle( ::CreateEvent( lpEventAttributes,
bManualReset,
bInitialState,
lpName ),
delete_on_release ) )
{
};
BOOL SetEvent()
{
_ASSERT( NULL != handle_ && NULL != handle_.get() );
return ::SetEvent( handle_.get() );
};
private:
boost::shared_ptr< CHandle > handle_;
}; // class CEvent
Or, is there a better way? (Note that I still want to maintain the copy semantics of the CHandle given by boost::shared_ptr<>.
Thanks, PaulH
I won't get into discussion on boost::shared_ptr or any smart ptr. And here is a few reasons why, from different angles between the lines, and why smart pointers can always and always be made redundant or beaten out.
Code does seem to emulate the CLR and NT model in which case there are predefined semantics by the OS for what you're doing. It's called ::DuplicateHandle and it works well and is more suited for cross process scenarios (and would hack less than boost::interprocess or similar). And it is applicable to few other contexts.
Now the second, hopefully not a contraversial bit where the poor-old-OO inheritance is neglected because containment focus wins out regurarly (yet it has nothing to do with OO really when you play mix-ins for those that continously scream: contain me ). So no matter how rare it might be, or whether it is an OO, non-OO or O(o) argument : "inheritance" wins here.
Why? Because it is a waitable handle concept, including the Win32 Event, Mutex, Auto reset kinds, Thread, all of it apart from critical_section (which also has a backing handle deep within - but is specially treated in both CLR and NT, plus it has dual-nature ). Thus it makes absolute sense for:
typedef CHandleT< HANDLE > WaitHandle;
to be the root of the "hierarchy", along with copy semantics of what the underlying implementation already is.
Lastly, it ends up the most efficient representation of your data types that are handles, as they will mimic the OS you are targetting and need no ref count or avalanche/rippling ref count either.
Then came cross-platform development and boost::thread-ing and ruined the bed-time story :-)
You don't need to store the handle in CEvent as a shared_ptr. The handle is already shared through CHandleT attribute.
Composition is fine as long as you will not want to use CEvent elements as CHandleT ones (using polymorphism).
精彩评论