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;

