Introduction
Sometimes when we pass data from one module to another, we face the problem with the quants. The 'receiver' object needs portions of X bytes of data but the 'sender' can only provide Y byte portions. So the 'sender' can't pass data directly to the 'receiver'.
There is another case. The 'receiver' needs data at an exact time (like CD/DVD burning device) but the 'sender' doesn't care.
So here, we need a FIFO buffer. FIFO stands for First-In - First-Out. The first data written to the buffer is the first data to be read and then removed. Then, the 'receiver' reads as much data as it likes (of course, if there is enough in the FIFO) and reads it whenever it likes after it has been written.
CTypedFifo
is a simple template class providing the functionality of a FIFO buffer and it's intended for storing any kind of object. It also has the ability to synchronize with other objects. This class pretends to be thread-safe.
Using the code
Here is the CTypedFifo
class declaration:
template <typename T>
class CTypedFifo
{
private:
T * m_pW;
T * m_pR;
T * m_pBuf;
T * m_pEnd;
CCriticalSection m_cs;
CEvent m_eNewData;
public:
CTypedFifo();
CTypedFifo(const UINT nCnt);
~CTypedFifo();
BOOL Init(const UINT nCnt);
BOOL Realloc(const UINT nCnt);
void Free();
int GetCount();
int GetCapacity();
int Read(T * pBuf, UINT nCnt);
void Write(const T * pBuf, UINT nCnt);
void Drain(const UINT nCnt);
DWORD WaitForNewData(DWORD dwMilliseconds = INFINITE);
HANDLE GetNewDataEventHandle() const {return m_eNewData.m_hObject;}
private:
int _Read(T * pBuf, UINT nCnt);
int _GetCount() const;
void _Drain(const UINT nCnt);
};
First, when you construct a CTypedFifo
object, you need to specify its capacity. This is the maximum amount of objects which can be stored in the buffer (not the size in bytes). If you use the default constructor, then you must call Init(const UINT nCnt)
before any Read
or Write
calls.
You can change the capacity of the buffer at anytime by calling Realloc(const UINT nCnt)
. The data stored in the buffer won't be lost.
You can retrieve the count of elements stored by calling GetCount()
, and if you have 'forgotten' the capacity of the buffer, you can get it using GetCapacity()
.
For example, if you want to write a sequence of 5 int
s to the buffer, the following code will do the job:
CTypedFifo<int> intFifo;
...
int seq[] = { 1, 3, 5, 7, 9 };
intFifo.Write(seq, 5);
Later, when you need to read 3 of them:
...
int read_seq[3];
int read_one;
intFifo.Read(read_seq, 3);
intFifo.Read(&read_one, 1);
So now, in read_seq
, we have the numbers { 1, 3, 5 }, and in read_one
, 7. Only one element is left in the FIFO buffer: 9.
If you don't need some of the data in the buffer anymore, you can simply call Drain()
and it will vanish. In order to prevent memory leaks, be sure if you store pointers in the FIFO to delete the objects they point to.
If there is no data in the FIFO, you can wait for new data using the WaitForNewData(DWORD dwMilliseconds = INFINITE)
method. It will lock the calling thread until the time interval in dwMilliseconds
elapses or another thread has written new data to the buffer.
When you need to wait for multiple objects, you can get a handle of an event object by calling GetNewDataEventHandle()
and using it in the WaitForSingleObject
or WaitForMultipleObjects
APIs.
That's all. I hope you enjoy it.