std::unique_ptr mit Windows HANDLE



  • Hey Leute,

    ich wollte Windows HANDLEs (nicht nur HANDLE sonder auch zb SOCKET usw) mit unique_ptr benutzen.

    Dafuer hab ich mir eine handleDeleter classe geschrieben:

    template<typename T, T invalidValue, typename D>
    class handleDeleter {
    
    public:
    	handleDeleter(D del) : m_del( del ) {};
    
    	void operator()(T p) const{
    		if( p != invalidValue)
    			m_del( p );
    	}
    private:
    	D m_del; 
    };
    
    handleDeleter<HANDLE, NULL, decltype(&CloseHandle) > processDeleter( CloseHandle );
    

    Jetzt wollte ich diesen deleter mit einem unique_ptr benutzen:

    std::unique_ptr<HANDLE> proc( GetCurrentProcess(), processDeleter);
    

    Jetzt hab ich aber das Problem, das HANDLE ein typedef auf void* ist und somit der unique_ptr ein void** will, was ich ja ned haben will.

    Heisst das jetzt das ich mir selber eine Klasse "smartHandle" basteln muss oder gibts da einen weg das mit HANLDE, SOCKET usw zu nutzen.

    Gruessle stuxn



  • Es geht, mit unique_ptr<void>. (Der will dann ein void* haben. ;)) Aber eine unique_handle Klasse macht durchaus Sinn (insbesondere für z.B. Sockets ohne gecaste.) Moment, irgendwo hatte ich sowas doch mal..

    template <typename H, H InvalidHandle, typename D>
    class unique_handle
    {
    	H h_;
    	D d_;
    
    public:
    	explicit unique_handle(H h, D d = D())
    		: h_(h)
    		, d_(d)
    	{}
    
    	unique_handle(unique_handle&& other)
    		: h_(other.h_)
    		, d_(other.d_)
    	{
    		other.h_ = InvalidHandle;
    	}
    
    	~unique_handle()
    	{
    		d_(h_);
    	}
    
    	unique_handle& operator = (unique_handle&& other)
    	{
    		swap(other);
    	}
    
    	//explicit operator H() const
    	//{
    	//	return get();
    	//}
    
    	H get() const
    	{
    		return h_;
    	}
    
    	void swap(unique_handle& other)
    	{
    		std::swap(h_, other.h_);
    		std::swap(d_, other.d_);
    	}
    
    private:
    };
    

    Ich bin mir sicher auch noch eine vollständige Version gehabt zu haben.. finde sie aber gerade nicht.. na ja, sollte dir aber trotzdem helfen.



  • cooky451 schrieb:

    ...

    unique_handle& operator = (unique_handle&& other)
    	{
    		swap(other);
    	}
    
    	void swap(unique_handle& other)
    	{
    		std::swap(h_, other.h_);
    		std::swap(d_, other.d_);
    	}
    
    private:
    };
    

    Ich bin mir sicher auch noch eine vollständige Version gehabt zu haben.. finde sie aber gerade nicht.. na ja, sollte dir aber trotzdem helfen.

    Ja hat mir auf jeden Fall geholfen,aber ich haette da noch ein Frage bezuglich des move assigment operators.
    Warum verwendes du da swap? sollte dann das other nicht invalidHandle beinhalten? Und "return *this" fehlt da oder?

    Oder is das einfach schnell ausn Kopf hingeschrieben und nicht das was du selber benutzt.

    Ich hab jetzt mal mit dem Vorbild von dir mein smartHandle bebaut:

    // smart handle
    #include <WinSock2.h>
    #include <Windows.h>
    #include <boost\noncopyable.hpp>
    
    template<typename T, typename D, D d_>
    struct handleDeleter {
    	void operator()(T p) const{
    		d_( p );
    	}
    };
    
    typedef handleDeleter<HANDLE, decltype(&CloseHandle), CloseHandle > processDeleter;
    typedef handleDeleter<SOCKET, decltype(&closesocket), closesocket> socketDeleter;
    
    template<typename T, T invalidHandle, typename D>
    class smartHandle : boost::noncopyable
    {
    public:
    	typedef T type;
    
    	explicit smartHandle(T value = invalidHandle , D d_ = D() ) :
    		m_rawHandle(value), m_closeFunc(d_)
    	{
    		std::cout << "smartHandle::smartHandle(T,D)\n";
    	}
    	smartHandle(smartHandle&& rhs)
    		: m_rawHandle( rhs.m_rawHandle ), m_closeFunc( rhs.m_closeFunc )
    	{
    		std::cout << "smartHandle::smartHandle( smartHandle&&) \n";
    		rhs.m_rawHandle = invalidHandle;
    	}
    	~smartHandle() {
    		std::cout << "smartHandle::~smartHandle\n";
    		close();
    	}
    
    	smartHandle& operator= (smartHandle&& rhs) {
    		std::cout << "smartHandle::operator= (smartHandle&&)\n";
    		close();		
    		std::swap(m_rawHandle, rhs.m_rawHandle );
    		std::swap(m_closeFunc, rhs.m_closeFunc);
    		return *this;
    	}
    
    	T get() const { return m_rawHandle; };
    
    	void reset(T newValue) { 
                    close();
    		m_rawHandle = newValue;
    	};
    private:
    
    	void close() {
    
    		if( m_rawHandle != invalidHandle) {
    			m_closeFunc( m_rawHandle);
    			m_rawHandle = invalidHandle;
    			std::cout << "smartHandle::close\n";
    		}
    	}
    
    	T m_rawHandle;
    	D m_closeFunc;
    };
    
    typedef smartHandle<HANDLE, NULL, processDeleter> smartProcessHandle;
    
    // main 
    
    #include "smartHandle.h"
    
    int main(int argc, char** argv) {
    
    	smartProcessHandle proc( GetCurrentProcess() );
    	smartProcessHandle newProc( std::move(proc) );
    
    	proc = std::move( newProc );
    
    	return 0;
    }
    

    Passt des dann soweit oder fehlt da noch was wichtiges?

    Gruessle & danks stuxn



  • stuxn schrieb:

    Warum verwendes du da swap?

    Versuch mal den Assignment Operator ohne Swap zu schreiben (vergiss nicht auf this == &other zu testen), dann weist du warum. 😃 Geht einfach viel leichter.

    stuxn schrieb:

    sollte dann das other nicht invalidHandle beinhalten?

    Ginge auch, aber wie gesagt, das ist viel komplizierter.

    stuxn schrieb:

    Und "return *this" fehlt da oder?

    Jo. Ist wie gesagt leider das einzige was ich gerade gefunden habe. 🙂 Abgesehen davon dass dein Code keinem einheitlichen Stil folgt, fällt mir jetzt nichts weiter auf. Ach ja, du hast kein swap.



  • cooky451 schrieb:

    stuxn schrieb:

    Warum verwendes du da swap?

    Versuch mal den Assignment Operator ohne Swap zu schreiben (vergiss nicht auf this == &other zu testen), dann weist du warum. 😃 Geht einfach viel leichter.

    stuxn schrieb:

    sollte dann das other nicht invalidHandle beinhalten?

    Ginge auch, aber wie gesagt, das ist viel komplizierter.

    Aber zu deinem Code:

    unique_handle& operator = (unique_handle&& other)
        {
            swap(other);
        }
    
        void swap(unique_handle& other)
        {
            std::swap(h_, other.h_);
            std::swap(d_, other.d_);
        }
    

    Du swaps doch hier einfach die 2 Handles ohne auf der rechten Seite auf "invalid" zu setzen.
    Aber mit dem move will ich doch sagen:
    Weis dem linken Objekt das Handle vom rechten Objekt zu. Wenn das linke Objekt einen validen Handle beinhaltet schließe diesen.
    Weil wenn ich einen unique_ptr move geh ich doch auch davon aus das dan der rechte auf nichts mehr Zeigt.
    Oder sehe ich da was falsch?
    €: Ich hab das auch von der MSDN Seite ein bissle kopiert: Move constructor

    cooky451 schrieb:

    stuxn schrieb:

    Und "return *this" fehlt da oder?

    Jo. Ist wie gesagt leider das einzige was ich gerade gefunden habe. 🙂 Abgesehen davon dass dein Code keinem einheitlichen Stil folgt, fällt mir jetzt nichts weiter auf. Ach ja, du hast kein swap.

    Ja das faellt mir auch immer wieder auf gerade wenns um Konstanten oder typedefs geht.
    Beispiel teilweise schreib ich

    typedef uint32_t addr_t
    

    und dann wieder:

    typedef uint32_t addr_type
    

    Aber was genau faellt dir da jetzt auf?

    Und warum brauch ich ein swap? Braucht man das fuer irgendein Algorithmus in der stl oder aehnlichem? Weil ich benutz ja nur std::swap fuer die Handles in "operator=(smartHandle&&)".

    Gruessle



  • Du kannst die Handles einfach swappen, weil 'other' sich auf ein bereits sterbendes (RValue Referenz) Objekt bezieht. 'other' ist dann fuer die Zerstoerung des alten *this zustaendig.


Anmelden zum Antworten