C言語でオブジェクト指向"風"のリングバッファと移動平均の計算
STMicroを使った仕事をしていて、なんとなく書いてみたくなったのでやってみた。
基本的には構造体を駆使すれば、オブジェクト指向風な書き方はできるみたい。
ただ、あくまでもオブジェクト指向”風”なんでね。
Contents
リングバッファの設計
#ifndef INC_TSRINGBUFFER_H_ #define INC_TSRINGBUFFER_H_ #include <stdlib.h> typedef struct ringbuffer{ unsigned long Count; unsigned long WriteAddress; unsigned long ReadAddress; void (*WriteData)(struct ringbuffer* this, double data); double (*ReadData)(struct ringbuffer* this); double (*ReadOldData)(struct ringbuffer* this); double *Buffer; }TTscRingBuffer; TTscRingBuffer *RingBuffer_Create(unsigned long Length); unsigned long RingBuffer_IncAddress(unsigned long adr, unsigned long length); void RingBuffer_WriteData(TTscRingBuffer* this, double data); double RingBuffer_ReadData(TTscRingBuffer* this); double RingBuffer_ReadOldData(TTscRingBuffer* this); #endif /* INC_TSRINGBUFFER_H_ */
まずはヘッダから。
typedef struct ringbuffer{ unsigned long Count; unsigned long WriteAddress; unsigned long ReadAddress; void (*WriteData)(struct ringbuffer* this, double data); double (*ReadData)(struct ringbuffer* this); double (*ReadOldData)(struct ringbuffer* this); double *Buffer; }TTscRingBuffer;
この部分でいわゆるオブジェクトを定義します。
メンバ変数やメソッドの定義ですね。
メソッドの実態は.cファイルの方に記述していくことになります。
プロトタイプ宣言はヘッダに書いてしまいます。
TTscRingBuffer *RingBuffer_Create(unsigned long Length); unsigned long RingBuffer_IncAddress(unsigned long adr, unsigned long length); void RingBuffer_WriteData(TTscRingBuffer* this, double data); double RingBuffer_ReadData(TTscRingBuffer* this); double RingBuffer_ReadOldData(TTscRingBuffer* this);
でソース部分
#include "TsRingBuffer.h" TTscRingBuffer *RingBuffer_Create(unsigned long Length){ TTscRingBuffer *buf = malloc(sizeof(TTscRingBuffer)); if (buf != NULL){ buf->Count = Length; buf->WriteAddress = 0; buf->ReadAddress = 0; buf->Buffer = malloc(sizeof(double)*Length); buf->WriteData = RingBuffer_WriteData; buf->ReadData = RingBuffer_ReadData; buf->ReadOldData= RingBuffer_ReadOldData; if (buf->Buffer == NULL){ free(buf); return NULL; } } return buf; } unsigned long RingBuffer_IncAddress(unsigned long adr, unsigned long length){ adr++; if (adr >= length){ adr = 0; } return adr; } void RingBuffer_WriteData(TTscRingBuffer* this, double data){ this->Buffer[this->WriteAddress] = data; this->WriteAddress = RingBuffer_IncAddress(this->WriteAddress, this->Count); } double RingBuffer_ReadData(TTscRingBuffer* this){ double cash; cash = this->Buffer[this->ReadAddress]; this->ReadAddress = RingBuffer_IncAddress(this->ReadAddress, this->Count); return cash; } double RingBuffer_ReadOldData(TTscRingBuffer* this){ return this->Buffer[this->WriteAddress]; }
ソースはこんな感じ。
それぞれを軽く解説しておきますと
#include "TsRingBuffer.h" TTscRingBuffer *RingBuffer_Create(unsigned long Length){ TTscRingBuffer *buf = malloc(sizeof(TTscRingBuffer)); if (buf != NULL){ buf->Count = Length; buf->WriteAddress = 0; buf->ReadAddress = 0; buf->Buffer = malloc(sizeof(double)*Length); buf->WriteData = RingBuffer_WriteData; buf->ReadData = RingBuffer_ReadData; buf->ReadOldData= RingBuffer_ReadOldData; if (buf->Buffer == NULL){ free(buf); return NULL; } } return buf; }
これがコンストラクタになります。
この中で変数の初期化と、配列の初期化をやっています。
メソッドのアドレスもここで代入します。
unsigned long RingBuffer_IncAddress(unsigned long adr, unsigned long length){ adr++; if (adr >= length){ adr = 0; } return adr; } void RingBuffer_WriteData(TTscRingBuffer* this, double data){ this->Buffer[this->WriteAddress] = data; this->WriteAddress = RingBuffer_IncAddress(this->WriteAddress, this->Count); } double RingBuffer_ReadData(TTscRingBuffer* this){ double cash; cash = this->Buffer[this->ReadAddress]; this->ReadAddress = RingBuffer_IncAddress(this->ReadAddress, this->Count); return cash; } double RingBuffer_ReadOldData(TTscRingBuffer* this){ // return this->Buffer[RingBuffer_IncAddress(this->WriteAddress, this->Count)]; return this->Buffer[this->WriteAddress]; }
メソッドの記述です。
pythonのselfのような感じで、基本的にメソッドの第一引数には自身の構造体を与えてやります。
移動平均計算
リングバッファと似たような感じで定義していきます。
まずヘッダ。
#ifndef INC_TSAVERAGE_H_ #define INC_TSAVERAGE_H_ #include "TsRingBuffer.h" typedef struct moveavg{ double Value; unsigned long NowCount; TTscRingBuffer* Buffer; double (*AddData)(struct moveavg* this, double data); void (*Clear)(struct moveavg* this); }TTscMovingAverage; TTscMovingAverage *MovingAverage_Create(unsigned long Length); double MovingAverage_AddData(TTscMovingAverage* this, double data); void MovingAverage_Clear(TTscMovingAverage* this); #endif /* INC_TSAVERAGE_H_ */
次にソース。
#include "TsAverage.h" //#include "stdio.h" TTscMovingAverage *MovingAverage_Create(unsigned long Length){ TTscMovingAverage *avg = malloc(sizeof(TTscMovingAverage)); if(avg!=NULL){ avg->NowCount = 0; avg->Value = 0; avg->Buffer = RingBuffer_Create(Length); avg->AddData= MovingAverage_AddData; avg->Clear = MovingAverage_Clear; if (avg->Buffer == NULL){ free(avg); return NULL; } } return avg; } double MovingAverage_AddData(TTscMovingAverage* this, double data){ // unsigned long n; if (this->NowCount >= this->Buffer->Count){ this->Value = ((this->Value*this->Buffer->Count) -this->Buffer->ReadOldData(this->Buffer) +data) / this->Buffer->Count; } else{ this->Value = ((this->Value*this->NowCount) +data) / (this->NowCount+1); this->NowCount++; } this->Buffer->WriteData(this->Buffer,data); return this->Value; } void MovingAverage_Clear(TTscMovingAverage* this){ this->NowCount = 0; this->Value = 0; }
移動平均の計算アルゴリズムは、以前解説した記事を見てください。
birdhouse.hateblo.jp
使い方例
雑な使い方ですが、こんな感じで使います。
#include <stdio.h> #include "TsAverage.h" int main(void){ TTscMovingAverage* avg = MovingAverage_Create(10); int i; for(i=0;i<20;i++){ printf("%f\n", avg->AddData(avg, i)); } }
出力はこんな感じで、移動平均が計算できてるのが確認できます。
0.000000 0.500000 1.000000 1.500000 2.000000 2.500000 3.000000 3.500000 4.000000 4.500000 5.500000 6.500000 7.500000 8.500000 9.500000 10.500000 11.500000 12.500000 13.500000 14.500000
ディスカッション
コメント一覧
まだ、コメントがありません