A long time ago, STL had auto_ptr<Type> class that automatically deleted a dynamically allocated C++ object when control leaves a block. Personally, I believe that auto_ptr<Type> was typically used in simple scenarios as a local variable or a class member but theoretically it is even possible to declare a vector of auto_ptr<Type> because auto_ptr<Type> stores an ownership indicator and its copy constructor transfers the ownership from the instance being copied, so vector::push_back(…) and vector::resize(…) functions works correctly.
About 10 years ago when I worked on some C++ projects I written a simple template class TOwner<A> that is a generalization of auto_ptr<Type>. TOwner<A> owns an abstract resource described by an adapter of type A. It stores a handle of type A::Handle and releases the ownership of the resource designated by the handle by calling A::Destroy(Handle h) function. Approximately at the same time some other guy Cristian Vlasceanu did the same work, see his article Generalizing the Concepts Behind auto_ptr.
Below I provided the source code of TOwner<A> class and the source code of some adapters for resources of various types. They all are enclosed in awl namespace:
template <class A> class TOwner { public: typedef A Adapter; typedef typename A::Handle Handle; static Handle Null() { return A::GetNull();} //The default constructor sets the stored handle to null and ownership indicator to true. explicit TOwner(const A & a = A()) : m_Adapter(a), m_bOwns(true), m_Handle(A::GetNull()) {} //An owner constructed with a handle to an object owns the object. TOwner(Handle h, const A & a = A()) : m_Adapter(a), m_bOwns(true), m_Handle(h) { } //Sets the stored handle to h and the ownership indicator to bOwns. TOwner(Handle h, bool bOwns, const A & a = A()) : m_Adapter(a), m_bOwns(bOwns), m_Handle(h) {} //The copy constructor sets the stored handle to the corresponding member of w and //transfers ownership from w. TOwner(const TOwner<A> & w) : m_Adapter(w.m_Adapter), m_bOwns(w.m_bOwns), m_Handle(w.Release()) {} TOwner<A> & operator = (Handle h) { if (m_Handle != h) { Destroy(); //delete the owned object m_Handle = h; //takes hold of the specified object } return *this; } TOwner<A> & operator = (const TOwner<A> & w) { if (this != &w) { Assign(w.m_Handle, w.m_bOwns, w.m_Adapter); w.Release(); } return *this; } void Assign(Handle h, bool bOwns, const A & a = A()) { if (m_Handle != h) { //delete the owned object Destroy(); m_Handle = h; //copy the adapter m_Adapter.operator = (a); } //transfer the ownership m_bOwns = bOwns; } ~TOwner() { Destroy();} //Indicates whether the stored handle is null. bool IsEmpty() const { return m_Handle == A::GetNull();} //Retrieves the stored handle value. Handle GetHandle() const { return m_Handle;} //Clears the ownership indicator returning the stored handle value. Handle Release() const { (const_cast<TOwner<A> *>(this))->m_bOwns = false; return m_Handle; } //Detaches the owned object without deleting them by setting the stored handle to null and //the ownership indicator to true. Returnes the handle to object to be detached. Handle Detach() { Handle h = m_Handle; CleanUp(); return h; } //Deletes the object that it owns. void Destroy() { if (m_bOwns && !IsEmpty()) AWL_VERIFY(m_Adapter.Destroy(m_Handle)); CleanUp(); } operator Handle () const { return m_Handle;} //The assert on operator& usually indicates a resource leak. //If this is really what is needed, however, call Detach() and try again. Handle * operator & () { AWL_ASSERT(m_bOwns); AWL_ASSERT(IsEmpty()); return &m_Handle; } Handle operator->() const { Handle h = GetHandle(); AWL_ASSERT(h != Null()); return h; } private: //Declaration order of the data members is significant because Release() //function used in initialization list produces a side effect. Adapter m_Adapter; //adapter to operate on the owned object bool m_bOwns; //ownership indicator Handle m_Handle; //owned object handle //This member function is used instead of Detach() to avoid an operation //with a dangling handle. void CleanUp() { m_bOwns = true; m_Handle = A::GetNull(); } };
Adapters and helper classes for working with GDI objects
template <class H> class AGdiObject : public TAdapter< H > { public: typedef typename TAdapter<H>::Handle Handle; bool Destroy(Handle h) { return ::DeleteObject(h) != 0;} }; template <class H> class HGdiObject : public TOwner < AGdiObject < H > > { private: typedef TOwner < AGdiObject < H > > Base; public: typedef typename Base::Adapter Adapter; typedef typename Base::Handle Handle; HGdiObject() {} HGdiObject(Handle h) : Base(h) {} HGdiObject(Handle h, bool bOwns) : Base(h, bOwns) {} HGdiObject(const HGdiObject & w) : Base(w) {} HGdiObject & operator=(Handle h) { Base::operator=(h); return *this; } HGdiObject & operator=(const HGdiObject & w) { Base::operator=(w); return *this; } }; typedef HGdiObject<HRGN> HRegion; typedef HGdiObject<HPEN> HPen; typedef HGdiObject<HBRUSH> HBrush; typedef HGdiObject<HBITMAP> HBitmap; typedef HGdiObject<HFONT> HFont; class TGdiSelector { protected: HDC m_hDC; public: TGdiSelector(HDC hDC) : m_hDC(hDC) { AWL_ASSERT(hDC != NULL); } }; template <class H> class HGdiEntry : public TGdiSelector { protected: H m_hOldObj; public: HGdiEntry(HDC hDC) : TGdiSelector(hDC) { m_hOldObj = static_cast<H>(::GetCurrentObject(hDC, HGdiObjectTraits<H>::GetObjectType())); AWL_ASSERT(m_hOldObj != NULL); } HGdiEntry(HDC hDC, H hObj) : TGdiSelector(hDC) { AWL_ASSERT(hObj != NULL); m_hOldObj = static_cast<H>(::SelectObject(m_hDC, hObj)); AWL_ASSERT(m_hOldObj != NULL); } ~HGdiEntry() { AWL_VERIFY(::SelectObject(m_hDC, m_hOldObj) != NULL);} H operator = (H hObj) { AWL_ASSERT(hObj != NULL); H hOldObj = static_cast<H>(::SelectObject(m_hDC, hObj)); AWL_ASSERT(hOldObj != NULL); return hOldObj; } }; typedef HGdiEntry<HPEN> HPenEntry; typedef HGdiEntry<HBRUSH> HBrushEntry; typedef HGdiEntry<HBITMAP> HBitmapEntry; typedef HGdiEntry<HFONT> HFontEntry; template <class H> class HGdiAttribute { private: typedef H Handle; HGdiObject<H> m_Owner; HGdiEntry<H> m_Selector; public: HGdiAttribute(HDC hDC) : m_Selector(hDC) { } HGdiAttribute(HDC hDC, const HGdiObject<H> & w) : m_Owner(w), m_Selector(hDC, w) {} HGdiAttribute(HDC hDC, Handle h, BOOL bOwns) : m_Owner(h, bOwns), m_Selector(hDC, h) {} H operator = (H hObj) { m_Selector = hObj; m_Owner = hObj; return hObj;} const HGdiObject<H> & operator = (const HGdiObject<H> & w) { m_Selector = w; m_Owner = w; return m_Owner;} operator H () const { return m_Owner;} operator const HGdiObject<H> & () const { return m_Owner;} }; typedef HGdiAttribute<HPEN> HSelPen; typedef HGdiAttribute<HBRUSH> HSelBrush; typedef HGdiAttribute<HBITMAP> HSelBitmap; typedef HGdiAttribute<HFONT> HSelFont;
So, what for did I do that? The primary reason is to get rid of the annoying MFC classes such as CDC, CPen and CBrush that draw an ellipse with 7 lines of code. Furthermore, and this probably the worst, they are do not guarantee that the old GDI objects will be selected back if an exception is thrown:
// Let's assume that we have some CDC at this point CDC *pDC = GetWindowDC(); // Create a solid gren brush. CBrush brush(RGB(0, 255, 0)); // Create a solid red pen of width 2. CPen pen(PS_SOLID, 2, RGB(255,0,0)); //Now I need select my objects to DC CBrush* pOldBrush = pDC->SelectObject(&brush); CPen* pOldPen = pDC->SelectObject( &pen ); pDC->Ellipse(CRect(100, 100, 200, 200)); // and then reselect stored objects. pDC->SelectObject( pOldPen ); pDC->SelectObject(pOldBrush);
If I use HSelBrush and HSelPen I need only 3 lines:
// Let's assume that we have some HDC at this point HDC hDC = ::GetWindowDC(hSomeWnd); // Create a solid gren brush and select it to DC. awl::HSelBrush hBrush(hDC, awl::CreateSolidBrush(RGB(0, 255, 0))); // Create a solid gren brush and select it to DC awl::HSelPen hPen(hDC, awl::CreatePen(PS_SOLID, 2, RGB(0, 255, 0))); ::Ellipse(hDC, CRect(100, 100, 200, 200)); //All work is done! //I can even draw another ellipse with only 2 lines of code: // Create and select another Pen, old pen will be deselected and deleted automatically hPen = awl::CreatePen(PS_SOLID, 2, RGB(0, 0, 255)); ::Ellipse(hDC, CRect(150, 150, 250, 250)); //GDI objects will be deselected and deleted automatically by destructors of hBrush and hPen
Adapters for Windows Kernel Objects
class AKernelObject : public TAdapter<HANDLE> { public: typedef TAdapter<HANDLE>::Handle Handle; bool Destroy(Handle h) { return ::CloseHandle(h) != 0;} }; typedef TOwner<AKernelObject> HKernelObject; typedef HKernelObject HFileMapping; typedef HKernelObject HThread;