背景
仕事でプログラミングする際に、リングバッファを多用するため、テンプレートを作成する
記事の目的
リングバッファのテンプレートを作成する
リングバッファ
ここでは、リングバッファの記述方法について記載する。
仕様
テンプレートの仕様について記載する。- 複数のスレッドから、バッファサイズの変更、バッファの書き込み、バッファの読み込みが可能
- データが格納されていないバッファにアクセスすると、エラーが表示され、デフォルトのデータが返される
テンプレート
リングバッファを使用するテンプレートを記載する。// ring_buffer.cpp
#include <stdio.h>
#include <mutex>
// リングバッファに格納するデータ形式
struct Data
{
int number = 0;
};
// 前方宣言
class RingBuffer
{
public:
// コンストラクタ(リングバッファのサイズを指定(2の冪乗が最適))
RingBuffer(uint buffer_size_in);
// デストラクタ
~RingBuffer();
// リングバッファのサイズを指定(0より大の整数、2の冪乗が最適)
void setBufferSize(uint buffer_size_in);
// リングバッファのサイズを取得
uint getBufferSize();
// リングバッファにデータを書き込み
void writeBuffer(Data data_in);
// リングバッファのデータを読み込み(0: 最新, BufferSize: 最古)
Data readBuffer(uint read_pointer_in);
private:
// コンストラクタ(宣言時に必ずバッファサイズを宣言する)
RingBuffer();
// リングバッファのポインタ
Data *data_;
// リングバッファのサイズ
uint buffer_size_ = 0;
// リングバッファ内に書き込まれたデータ数
uint data_size_ = 0;
// 書き込みポインタ(最新データのポインタ)
uint write_pointer_ = 0;
// mutex(読み書き、バッファサイズ変更を排他)
std::mutex mtx_;
};
// コンストラクタ(プライベート関数)
RingBuffer::RingBuffer()
{}
// コンストラクタ(リングバッファのサイズを指定)
RingBuffer::RingBuffer(uint buffer_size_in)
{
// リングバッファのサイズを指定、メモリ上にバッファを確保
setBufferSize(buffer_size_in);
}
// デストラクタ(リングバッファを削除)
RingBuffer::~RingBuffer()
{
if (buffer_size_ > 0)
{
delete[] data_;
}
}
// リングバッファのサイズを指定、メモリ上にバッファを確保
void RingBuffer::setBufferSize(uint buffer_size_in)
{
if (buffer_size_in > 0)
{
// 排他
std::lock_guard<std::mutex> lock(mtx_);
// すでにメモリ上にリングバッファが確保されている場合は、一旦削除
if (buffer_size_ > 0)
{
delete data_;
}
// リングバッファのサイズを保持
buffer_size_ = buffer_size_in;
// 内部変数の初期化
data_size_ = 0;
write_pointer_ = 0;
// メモリ上にリングバッファを確保
data_ = new Data[buffer_size_in];
}
else
{
fprintf(stderr, "Error: [RingBuffer] Please set buffer size more than 0.\n");
}
}
// リングバッファのサイズを取得
uint RingBuffer::getBufferSize()
{
// 排他
std::lock_guard<std::mutex> lock(mtx_);
return buffer_size_;
}
// リングバッファにデータ書き込み
void RingBuffer::writeBuffer(Data data_in)
{
// リングバッファが確保されている場合のみ処理
if (buffer_size_ > 0)
{
// 排他
std::lock_guard<std::mutex> lock(mtx_);
// 書き込みポインタを進める
write_pointer_ ++;
write_pointer_ %= buffer_size_;
// データを書き込み
data_[write_pointer_] = data_in;
// 書き込んだデータ数を保持
if (data_size_ < buffer_size_)
{
data_size_ ++;
}
}
else
{
fprintf(stderr, "Error: [RingBuffer] Buffer is not allocated.\n");
}
}
Data RingBuffer::readBuffer(uint read_pointer_in)
{
// 過去にデータが書き込まれていないデータのポインタにはアクセスさせない
if (read_pointer_in < data_size_)
{
// 排他
std::lock_guard<std::mutex> lock(mtx_);
// 現在の最新データのポインタを0, 古いほど値が大きくなる(最大: Buffer Size)
uint read_pointer = (buffer_size_+write_pointer_-read_pointer_in)%buffer_size_;
return data_[read_pointer] ;
}
else
{
fprintf(stderr, "Error: [RingBuffer] Read pointer is out of buffer. (%d / %d)\n",
read_pointer_in, data_size_);
Data data;
return data;
}
}
// テストコード
int main()
{
fprintf(stdout, "Error pattern -------\n");
// 宣言(エラー)
RingBuffer buf(0);
Data data_1;
data_1.number = 0;
// データ書き込み(エラー)
buf.writeBuffer(data_1);
// データ読み込み(エラー)
buf.readBuffer(1);
// リングバッファのサイズを再設定
buf.setBufferSize(10);
// データ読み込み(エラー)
buf.readBuffer(1);
fprintf(stdout, "Normal pattern ------\n");
for (uint i=0; i<2*buf.getBufferSize(); i++)
{
Data data_2;
data_2.number = i;
// データ書き込み(正常)
buf.writeBuffer(data_2);
fprintf(stdout, "%d / %d\n",i, 2*buf.getBufferSize()-1);
// 1. データ読み込み(最古のデータ 0 or 10)
fprintf(stdout, "1. buf.data[%d].number: %d\n",
i%buf.getBufferSize(),
buf.readBuffer(i%buf.getBufferSize()).number);
// 2. データ読み込み(最新のデータ 0 to 19)
fprintf(stdout, "2. buf.data[%d].number: %d\n",
0,buf.readBuffer(0).number);
}
return 0;
}
テンプレートのコンパイル方法
テンプレートのコンパイル方法を記載する。$ g++ ./ring_buffer.cpp -std=c++11 -o ring_buffer_sample
テンプレートの実行結果
テンプレートの実行結果を記載する。$ ./ring_buffer_sample
Error pattern -------
Error: [RingBuffer] Please set buffer size more than 0.
Error: [RingBuffer] Buffer is not allocated.
Error: [RingBuffer] Read pointer is out of buffer. (1 / 0)
Error: [RingBuffer] Read pointer is out of buffer. (1 / 0)
Normal pattern ------
0 / 19
1. buf.data[0].number: 0
2. buf.data[0].number: 0
1 / 19
1. buf.data[1].number: 0
2. buf.data[0].number: 1
2 / 19
1. buf.data[2].number: 0
2. buf.data[0].number: 2
3 / 19
1. buf.data[3].number: 0
2. buf.data[0].number: 3
4 / 19
1. buf.data[4].number: 0
2. buf.data[0].number: 4
5 / 19
1. buf.data[5].number: 0
2. buf.data[0].number: 5
6 / 19
1. buf.data[6].number: 0
2. buf.data[0].number: 6
7 / 19
1. buf.data[7].number: 0
2. buf.data[0].number: 7
8 / 19
1. buf.data[8].number: 0
2. buf.data[0].number: 8
9 / 19
1. buf.data[9].number: 0
2. buf.data[0].number: 9
10 / 19
1. buf.data[0].number: 10
2. buf.data[0].number: 10
11 / 19
1. buf.data[1].number: 10
2. buf.data[0].number: 11
12 / 19
1. buf.data[2].number: 10
2. buf.data[0].number: 12
13 / 19
1. buf.data[3].number: 10
2. buf.data[0].number: 13
14 / 19
1. buf.data[4].number: 10
2. buf.data[0].number: 14
15 / 19
1. buf.data[5].number: 10
2. buf.data[0].number: 15
16 / 19
1. buf.data[6].number: 10
2. buf.data[0].number: 16
17 / 19
1. buf.data[7].number: 10
2. buf.data[0].number: 17
18 / 19
1. buf.data[8].number: 10
2. buf.data[0].number: 18
19 / 19
1. buf.data[9].number: 10
2. buf.data[0].number: 19
備考
- boostでも同様の関数boost::circular_bufferが実装されている
まとめ
- C++でリングバッファを記述するテンプレートを記載した
参考文献
変更履歴
- 2019/08/28: 新規作成
- 2019/08/28: 備考追記
- 2019/09/02: 仕様追記
0 件のコメント:
コメントを投稿