2019/08/28

C++でリングバッファを実装

背景


仕事でプログラミングする際に、リングバッファを多用するため、テンプレートを作成する

記事の目的


リングバッファのテンプレートを作成する

リングバッファ


ここでは、リングバッファの記述方法について記載する。

仕様

テンプレートの仕様について記載する。
  • 複数のスレッドから、バッファサイズの変更、バッファの書き込み、バッファの読み込みが可能
  • データが格納されていないバッファにアクセスすると、エラーが表示され、デフォルトのデータが返される

テンプレート

リングバッファを使用するテンプレートを記載する。
  1. // ring_buffer.cpp
  2. #include <stdio.h>
  3. #include <mutex>
  4. // リングバッファに格納するデータ形式
  5. struct Data
  6. {
  7. int number = 0;
  8. };
  9. // 前方宣言
  10. class RingBuffer
  11. {
  12. public:
  13. // コンストラクタ(リングバッファのサイズを指定(2の冪乗が最適))
  14. RingBuffer(uint buffer_size_in);
  15. // デストラクタ
  16. ~RingBuffer();
  17. // リングバッファのサイズを指定(0より大の整数、2の冪乗が最適)
  18. void setBufferSize(uint buffer_size_in);
  19. // リングバッファのサイズを取得
  20. uint getBufferSize();
  21. // リングバッファにデータを書き込み
  22. void writeBuffer(Data data_in);
  23. // リングバッファのデータを読み込み(0: 最新, BufferSize: 最古)
  24. Data readBuffer(uint read_pointer_in);
  25. private:
  26. // コンストラクタ(宣言時に必ずバッファサイズを宣言する)
  27. RingBuffer();
  28. // リングバッファのポインタ
  29. Data *data_;
  30. // リングバッファのサイズ
  31. uint buffer_size_ = 0;
  32. // リングバッファ内に書き込まれたデータ数
  33. uint data_size_ = 0;
  34. // 書き込みポインタ(最新データのポインタ)
  35. uint write_pointer_ = 0;
  36. // mutex(読み書き、バッファサイズ変更を排他)
  37. std::mutex mtx_;
  38. };
  39. // コンストラクタ(プライベート関数)
  40. RingBuffer::RingBuffer()
  41. {}
  42. // コンストラクタ(リングバッファのサイズを指定)
  43. RingBuffer::RingBuffer(uint buffer_size_in)
  44. {
  45. // リングバッファのサイズを指定、メモリ上にバッファを確保
  46. setBufferSize(buffer_size_in);
  47. }
  48. // デストラクタ(リングバッファを削除)
  49. RingBuffer::~RingBuffer()
  50. {
  51. if (buffer_size_ > 0)
  52. {
  53. delete[] data_;
  54. }
  55. }
  56. // リングバッファのサイズを指定、メモリ上にバッファを確保
  57. void RingBuffer::setBufferSize(uint buffer_size_in)
  58. {
  59. if (buffer_size_in > 0)
  60. {
  61. // 排他
  62. std::lock_guard<std::mutex> lock(mtx_);
  63. // すでにメモリ上にリングバッファが確保されている場合は、一旦削除
  64. if (buffer_size_ > 0)
  65. {
  66. delete data_;
  67. }
  68. // リングバッファのサイズを保持
  69. buffer_size_ = buffer_size_in;
  70. // 内部変数の初期化
  71. data_size_ = 0;
  72. write_pointer_ = 0;
  73. // メモリ上にリングバッファを確保
  74. data_ = new Data[buffer_size_in];
  75. }
  76. else
  77. {
  78. fprintf(stderr, "Error: [RingBuffer] Please set buffer size more than 0.\n");
  79. }
  80. }
  81. // リングバッファのサイズを取得
  82. uint RingBuffer::getBufferSize()
  83. {
  84. // 排他
  85. std::lock_guard<std::mutex> lock(mtx_);
  86. return buffer_size_;
  87. }
  88. // リングバッファにデータ書き込み
  89. void RingBuffer::writeBuffer(Data data_in)
  90. {
  91. // リングバッファが確保されている場合のみ処理
  92. if (buffer_size_ > 0)
  93. {
  94. // 排他
  95. std::lock_guard<std::mutex> lock(mtx_);
  96. // 書き込みポインタを進める
  97. write_pointer_ ++;
  98. write_pointer_ %= buffer_size_;
  99. // データを書き込み
  100. data_[write_pointer_] = data_in;
  101. // 書き込んだデータ数を保持
  102. if (data_size_ < buffer_size_)
  103. {
  104. data_size_ ++;
  105. }
  106. }
  107. else
  108. {
  109. fprintf(stderr, "Error: [RingBuffer] Buffer is not allocated.\n");
  110. }
  111. }
  112. Data RingBuffer::readBuffer(uint read_pointer_in)
  113. {
  114. // 過去にデータが書き込まれていないデータのポインタにはアクセスさせない
  115. if (read_pointer_in < data_size_)
  116. {
  117. // 排他
  118. std::lock_guard<std::mutex> lock(mtx_);
  119. // 現在の最新データのポインタを0, 古いほど値が大きくなる(最大: Buffer Size)
  120. uint read_pointer = (buffer_size_+write_pointer_-read_pointer_in)%buffer_size_;
  121. return data_[read_pointer] ;
  122. }
  123. else
  124. {
  125. fprintf(stderr, "Error: [RingBuffer] Read pointer is out of buffer. (%d / %d)\n",
  126. read_pointer_in, data_size_);
  127. Data data;
  128. return data;
  129. }
  130. }
  131. // テストコード
  132. int main()
  133. {
  134. fprintf(stdout, "Error pattern -------\n");
  135. // 宣言(エラー)
  136. RingBuffer buf(0);
  137. Data data_1;
  138. data_1.number = 0;
  139. // データ書き込み(エラー)
  140. buf.writeBuffer(data_1);
  141. // データ読み込み(エラー)
  142. buf.readBuffer(1);
  143. // リングバッファのサイズを再設定
  144. buf.setBufferSize(10);
  145. // データ読み込み(エラー)
  146. buf.readBuffer(1);
  147. fprintf(stdout, "Normal pattern ------\n");
  148. for (uint i=0; i<2*buf.getBufferSize(); i++)
  149. {
  150. Data data_2;
  151. data_2.number = i;
  152. // データ書き込み(正常)
  153. buf.writeBuffer(data_2);
  154. fprintf(stdout, "%d / %d\n",i, 2*buf.getBufferSize()-1);
  155. // 1. データ読み込み(最古のデータ 0 or 10)
  156. fprintf(stdout, "1. buf.data[%d].number: %d\n",
  157. i%buf.getBufferSize(),
  158. buf.readBuffer(i%buf.getBufferSize()).number);
  159. // 2. データ読み込み(最新のデータ 0 to 19)
  160. fprintf(stdout, "2. buf.data[%d].number: %d\n",
  161. 0,buf.readBuffer(0).number);
  162. }
  163. return 0;
  164. }

テンプレートのコンパイル方法

テンプレートのコンパイル方法を記載する。
  1. $ g++ ./ring_buffer.cpp -std=c++11 -o ring_buffer_sample

テンプレートの実行結果

テンプレートの実行結果を記載する。
  1. $ ./ring_buffer_sample
  2. Error pattern -------
  3. Error: [RingBuffer] Please set buffer size more than 0.
  4. Error: [RingBuffer] Buffer is not allocated.
  5. Error: [RingBuffer] Read pointer is out of buffer. (1 / 0)
  6. Error: [RingBuffer] Read pointer is out of buffer. (1 / 0)
  7. Normal pattern ------
  8. 0 / 19
  9. 1. buf.data[0].number: 0
  10. 2. buf.data[0].number: 0
  11. 1 / 19
  12. 1. buf.data[1].number: 0
  13. 2. buf.data[0].number: 1
  14. 2 / 19
  15. 1. buf.data[2].number: 0
  16. 2. buf.data[0].number: 2
  17. 3 / 19
  18. 1. buf.data[3].number: 0
  19. 2. buf.data[0].number: 3
  20. 4 / 19
  21. 1. buf.data[4].number: 0
  22. 2. buf.data[0].number: 4
  23. 5 / 19
  24. 1. buf.data[5].number: 0
  25. 2. buf.data[0].number: 5
  26. 6 / 19
  27. 1. buf.data[6].number: 0
  28. 2. buf.data[0].number: 6
  29. 7 / 19
  30. 1. buf.data[7].number: 0
  31. 2. buf.data[0].number: 7
  32. 8 / 19
  33. 1. buf.data[8].number: 0
  34. 2. buf.data[0].number: 8
  35. 9 / 19
  36. 1. buf.data[9].number: 0
  37. 2. buf.data[0].number: 9
  38. 10 / 19
  39. 1. buf.data[0].number: 10
  40. 2. buf.data[0].number: 10
  41. 11 / 19
  42. 1. buf.data[1].number: 10
  43. 2. buf.data[0].number: 11
  44. 12 / 19
  45. 1. buf.data[2].number: 10
  46. 2. buf.data[0].number: 12
  47. 13 / 19
  48. 1. buf.data[3].number: 10
  49. 2. buf.data[0].number: 13
  50. 14 / 19
  51. 1. buf.data[4].number: 10
  52. 2. buf.data[0].number: 14
  53. 15 / 19
  54. 1. buf.data[5].number: 10
  55. 2. buf.data[0].number: 15
  56. 16 / 19
  57. 1. buf.data[6].number: 10
  58. 2. buf.data[0].number: 16
  59. 17 / 19
  60. 1. buf.data[7].number: 10
  61. 2. buf.data[0].number: 17
  62. 18 / 19
  63. 1. buf.data[8].number: 10
  64. 2. buf.data[0].number: 18
  65. 19 / 19
  66. 1. buf.data[9].number: 10
  67. 2. buf.data[0].number: 19

備考


まとめ


  • C++でリングバッファを記述するテンプレートを記載した

参考文献



変更履歴


  1. 2019/08/28: 新規作成
  2. 2019/08/28: 備考追記
  3. 2019/09/02: 仕様追記

0 件のコメント:

コメントを投稿

MQTTの導入

背景 IoTデバイスの接続環境構築のため、MQTT(mosquitto)の導入を行った。 記事の目的 MQTT(mosquitto)をUbuntuに導入する mosquitto ここではmosquittoについて記載する。 MQTT MQTT(Message Qu...