C言語でオブジェクト指向"風"のリングバッファと移動平均の計算

C,Programing

STMicroを使った仕事をしていて、なんとなく書いてみたくなったのでやってみた。

基本的には構造体を駆使すれば、オブジェクト指向風な書き方はできるみたい。
ただ、あくまでもオブジェクト指向”風”なんでね。

リングバッファの設計

#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

C,Programing

Posted by tsubakurame