Advice I: IsEmpty
bool IsEmpty() const
{
return !mIsFull && mReadIdx == mWriteIdx;
}
Why not deploy a field
size_t size = 0;
counting the number of elements in the buffer? That way, your IsEmpty becomes much simpler:
bool IsEmpty() const
{
return size == 0;
}
Advice II: Add IsFull
How about this?
bool IsFull() const {
return size == BUFFER_SIZE;
}
Advice III: Throwing appropriate exceptions
I would throw an std::underflow_error when trying to read from an empty buffer, and I would throw an std::overflow_error when trying to write to a full buffer.
Summa summarum
All in all, I thought about this implementation:
#include <cstdlib>
#include <cstdint>
#include <stdexcept>
#include <iostream>
class Fifo
{
public:
void Write(uint8_t data)
{
if (IsFull())
{
throw std::overflow_error("The buffer is full.");
}
mBuffer[headIndex++ % BUFFER_SIZE] = data;
headIndex %= BUFFER_SIZE;
size++;
}
uint8_t Read()
{
if (IsEmpty())
{
throw std::underflow_error("The buffer is empty.");
}
uint8_t value = mBuffer[headIndex++];
headIndex %= BUFFER_SIZE;
size--;
return value;
}
private:
bool IsEmpty() const
{
return size == 0;
}
bool IsFull() const {
return size == BUFFER_SIZE;
}
// Member variables
size_t headIndex = 0;
static constexpr int BUFFER_SIZE = 4;
uint8_t mBuffer[BUFFER_SIZE] = { 0 };
size_t size = 0;
};
int main()
{
Fifo fifo;
for (int i = 0; i < 4; i++)
{
fifo.Write(i + 2);
}
for (int i = 0; i < 6; i++)
{
std::cout << unsigned(fifo.Read()) << "\n";
}
fifo.Write(10);
return 0;
}
Hope that helps.