tags:

views:

122

answers:

2

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

+3  A: 

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

rama-jka toti
+1 for `DuplicateHandle`
Kirill V. Lyadvinsky
@Majk-a-Ra, why does inheritance wins? If he is using CHandleT just as a "scoped_handle" and does not need polymorphism. Less coupling option should be the winner.
fnieto
It was quoted and you do provide the right keyword: composition. Mix-ins are not coupling, on the contrary. You can replace the handle type centrally.and do plenty more. Just like headers and templates are not coupling. Those who adore their .cpp usually love the containment too :) Composition-based techniques are one of the most underestimated out there but: it isn't a problem in functional space. OO guys need to question that more, why not. Even in algorithms, sure, neither is useful but composition stands out in functional form, not inheriting or containing, not dealing with polymorphism..
rama-jka toti
@Majk-a-Ra So, if I understood you correctly, you're suggesting I should have CEvent derive from CHandle? Would CHandleT need a virtual destructor in that case? (Why?/Why not?) And, I'd love to know what you think of fnieto's question above. Thanks
PaulH
Regarding polymorphism, it has its uses and plenty of scenarios, but when it doesn't, it really does not have *any* :) I like containing typedefs for example and that's not containment at all :) But your right if one requires polymorphism. In this example we are dealing with an OS and it certainly does no OO at all. It uses what Stepanov terms a remarkable machine abstraction achievement: C. And while you can simulate polymorphism with it, and they often do in low-level code, C++ provies huge benefits to play with types and tremendously powerful or specific constraints.
rama-jka toti
I would use boost, seriously, all done. But if I was to do it for some reason, yes I would avoid usage fnieto mentions, and forbid polymorphism for this (it makes a lot of sense and you can always store ptrs in containers). So can you guarantee what fnieto specified below is the case? It depends, so your pick, but do lean towards containment if less experience devs will use that same code.
rama-jka toti
(all 'critical' of polymorphism bits above are related to runtime kind of course not compile-time). One great example: functors often use 'inheritance' and it looks pretty to me.
rama-jka toti
@Majk-a-Ra: To cite Dijkstra: 'How do we convince people that in programming simplicity and clarity are not a dispensable luxury, but a crucial matter that decides between success and failure?' Code should be simple, whether it is for John Beginner or Andrei Alexandrescu to read. What real advantages does your inheritance provide over composition? You state that functors use inheritance: they do usually but just for convenience: from empty classes that only provide typedefs and have no content...
David Rodríguez - dribeas
You should have started with a few lines example instead of being so verbose. Remember this is to help, not to show off.
fnieto
ok will delete all the comments.. you have a fair point but he got 10 solid examples how it beats the 'Coding Standards' shoehorning :)
rama-jka toti
I admit that my comment (the second one, now deleted) was offensive and I apologize for it. While I cannot agree in that all your points were solid, you do have a point.
David Rodríguez - dribeas
all good and not an issue at all.. it wouldn't be interesting or challenging if we all agreed to the same principles. and without wide, different angles it would be dry and boring fast imo. piece to all..
rama-jka toti
+2  A: 

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

fnieto