Download source files - 2 Kb
Introduction
I recently had a need to maintain a 'warm' list of objects. In my case, these were ADO RecordsetPtr
and CommandPtr
objects.
The code I was working on had a large number of routines that created a RecordsetPtr
, executed an SQL command, looked at
the results, and deleted the RecordsetPtr
. These routines were called again and again, but in no simple pattern.
In looking at the performance traces, I found that a surprising amount of time was spent creating the COM object instances,
then tearing them down. I quickly decided that what I needed was a simple way to keep these COM instances 'warm' and
available for reuse, and that such a mechanism should change the existing code as little as possible.
Obviously, what I needed was a lookaside list. I was surprised when I didn't find anything that quite fit the bill, and
decided to build a relatively simple generic lookaside list handler.
The result consists of two template classes: lookaside
,
which manages the set of warm objects, and simpleIniter
,
which is used to perform object-specific initialization and teardown
functions (remember that my goal was to minimize changes elsewhere in
the code, so I decided against forcing each contained object into a wrapper class).
A lookaside
instance accepts two template arguments in its
declaration: the type of the object to be stored, and the 'initer'
class, which is used to perform type specific initialization and
teardown operations (see below). The constructor also accepts two arguments:
the number of objects to pre-load into the lookaside list, and the maximum number of objects to ever hold in it.
lookaside
has two main methods, Get
and
Release
, along with a small number of supporting methods. Together, these are:
Get
returns a pointer to a warm
object, and will have created one, if necessary.
Release
returns an object to the lookaside list, and may delete the object.
Drain
deletes all objects currently held in the lookaside list.
GetCurSize
returns the number of objects currently held in the lookaside list.
GetMaxSize
returns the maximum number of objects that may be held in this lookaside list.
The base code also includes a default 'initer' class,
simpleIniter
which perform effectively null operations for
each of its four methods. These are:
init
is called just after the new operation that created the object
activate
is called just before returning the object to the consumer (via lookaside::Get
)
deactivate
is called just after the object is returned to the lookaside list via lookaside::Release
deinit
is called just before the delete operation to destory the object.
Normally all of these routines will be effectively null, and simpleIniter
covers these cases. However, as an example showing when these methods may be useful, consider my initial reason for building this class: maintaining a lookaside list for ADO objects. In that case, I built the following variant called ADOIniter
:
template<class T>
class ADOIniter
{
public:
ADOIniter () {};
bool init (T *p);
bool activate (T *p) {return true;};
bool deactivate (T *p) {return true;};
void deinit (T *p) {return; };
};
This class varies from simpleIniter
inasmuch as it has an active init
method, which is used to create the appropriate instance, e.g.:
bool ADOIniter<_CommandPtr>::init(_CommandPtr *p)
{
return SUCCEEDED (p -> CreateInstance (__uuidof (Command)));
};
bool ADOIniter<_RecordsetPtr>::init(_RecordsetPtr *p)
{
return SUCCEEDED (p -> CreateInstance (__uuidof (Recordset)));
};
These methods ensured that any object returned from the Get
function was ready for use, regardless whether it had just been created or not.
I've included the source for the lookaside
and simpleIniter
classes below, and they can also be found at the download point listed above.
#ifndef _LOOKASIDE_H_
#define _LOOKASIDE_H_
#include <list>
template<class T>
class simpleIniter
{
public:
simpleIniter () {};
bool init (T *p) {return true;};
bool activate (T *p) {return true;};
bool deactivate (T *p) {return true;};
void deinit (T *p) {return; };
};
template<class T, class A = simpleIniter<T> >
class lookaside
{
public:
lookaside (int nMin = 0, int nMax = 1)
{
m_nMax = nMax;
if (nMin > nMax) nMin = nMax;
FillToMin (nMin);
};
virtual ~lookaside ()
{
Drain ();
}
T *Get (void)
{
T *pRes;
if (!m_lstFreeElems.empty ())
{
pRes = m_lstFreeElems.front ();
m_lstFreeElems.pop_front ();
}
else
pRes = NewElem ();
if (!m_Initer.activate (pRes))
{
Release (pRes, true);
pRes = NULL;
}
return pRes;
};
void Release (T *p, bool fDontReuse = false)
{
fDontReuse |= !m_Initer.deactivate (p);
if (fDontReuse) DeleteEntry (p);
else m_lstFreeElems.push_back (p);
while (m_lstFreeElems.size () > m_nMax)
{
T *p = m_lstFreeElems.front ();
m_lstFreeElems.pop_front ();
DeleteEntry (p);
}
};
void Drain (void)
{
while (!m_lstFreeElems.empty ())
{
T *p = m_lstFreeElems.front ();
m_lstFreeElems.pop_front ();
DeleteEntry (p);
}
};
int GetCurSize () const {return m_lstFreeElems.size (); };
int GetMaxSize () const {return m_nMax; };
private:
int m_nMax;
std::list<T *> m_lstFreeElems;
A m_Initer;
T *NewElem ()
{
T *p = new T;
m_Initer.init (p);
return p;
};
void FillToMin (int nMin)
{
while (m_lstFreeElems.size () < nMin)
{
T *p = NewElem ();
m_lstFreeElems.push_back (p);
}
};
void DeleteEntry (T *p)
{
m_Initer.deinit (p);
delete p;
};
};
#endif