やろうとしていること。
- C言語でクラスを表現したい。
背景
- C言語のコンパイラしかない組み込み系などでは、UMLでクラス図書いても何かむなしい。
- よってC言語でもクラスっぽい表現で記述したい。
方針
- クラスはstructで表現。
- メンバはstructのメンバそのもの。
- 継承を実現するため、メソッドは関数ポインタをstructのメンバにして、初期化時にメソッド実体のポインタを渡す。
- クラス(struct)の初期化はマクロを用意する。
- メソッドの呼び出しも親側で呼び出しマクロを用意する。初期化時に与えられた関数ポインタにより、親子どちらかの適切な方のメソッド実体が呼び出される。
- privateメンバ、メソッドは.cファイル側にstaticにして記述し隠す。
- protectedはあきらめてpublicにする(ヘッダに書く)。
コード(ヘッダのみ)
親クラス
Controller.h
#ifndef _CONTROLLER_H_INCLUDED_
#define _CONTROLLER_H_INCLUDED_
#include "Common.h"
// クラス定義
struct Controller {
// メンバ変数
int data;
// メソッド
// 関数ポインタで表現
void ( *Initialize )( struct Controller* const self );
void ( *Execute )( struct Controller* const self );
};
/// コンストラクタ
extern void Controller_Constructor(
struct Controller* const self,
int data );
// メソッド実体定義
extern void Controller_Initialize( struct Controller* const self );
extern void Controller_Execute( struct Controller* const self );
/// メンバー初期化定義
#define Controller_InitMembers \
.data = 0,
/// メソッド初期化定義
#define Controller_InitMethods \
.Initialize = Controller_Initialize, \
.Execute = Controller_Execute, \
/// 初期化定義
#define Controller_Init \
{ \
Controller_InitMembers \
Controller_InitMethods \
}
// 仮想メソッドの呼び出し
#define Controller_initialize( self ) V_METHOD( self, Controller, Initialize )
#define Controller_execute( self ) V_METHOD( self, Controller, Execute)
#define _Controller( self ) ( (struct Controller*)self )
#endif
共通ヘッダ
共通ヘッダでの仮想メソッド呼び出しマクロを定義
Common.h
// Virtual Method General
#define V_METHOD( self, Type, Method ) ( (struct Type*)( self ) )->Method( (struct Type*)( self ) )
#define V_METHOD_ARG1( self, Type, Method, arg1 ) ( (struct Type*)( self ) )->Method( (struct Type*)( self ), arg1 )
#define V_METHOD_ARG2( self, Type, Method, arg1, arg2 ) ( (struct Type*)( self ) )->Method( (struct Type*)( self ), arg1, arg2 )
継承クラス(子クラス)
PanelController.h
#ifndef _PANEL_CONTROLLER_H_INCLUDED_
#define _PANEL_CONTROLLER_H_INCLUDED_
#include "Controller.h"
// 子クラス定義
struct PanelController {
// 親クラスを実体として持つ
struct Controller __baseClass;
// 子クラスのメンバ
int height;
// 子クラスのメソッド
int ( *GetHeight )( struct PanelController* const self );
};
// メンバ定義
extern void PanelController_Initialize( struct Controller* const self );
extern void PanelController_Execute( struct Controller* const self );
extern int PanelController_GetHeight( struct Controller* const self );
/// コンストラクタ
extern void PanelController_Constructor(
struct PanelController* const self,
int data,
int height );
/// 親クラス初期化定義
#define PanelController_InitParent \
{ \
Controller_InitMembers \
.Initialize = PanelController_Initialize, \
.Execute = PanelController_Execute, \
},
/// メンバー初期化定義
#define PanelController_InitMembers \
.height = 0,
/// メソッド初期化定義
#define PanelController_InitMethods \
.GetHeight = PanelController_GetHeight,
/// 初期化定義
#define PanelController_Init \
{ \
PanelController_InitParent \
PanelController_InitMembers \
PanelController_InitMethods \
}
#define _PanelController( self ) ( (struct PanelController*)self )
#endif
使用例
main.c
// 実体化
struct PanelController controller = PanelController_Init;
// コンストラクタ
PanelController_Constructor( &controller, 100, 200 );
// メソッド呼び出し
Controller_initialize( &controller );
Controller_execute( &controller );
参考
-
C言語でオブジェクト指向を表現する (クラス、継承) - Qiita
- コンストラクタ
- メソッド継承はなし
-
C 言語によるオブジェクト記述法 COOL
- メソッドの継承も含む
- ちょっと複雑
-
SMC: The State Machine Compiler
- StateMachineのコード生成にはSMCを使うので、SMCの吐き出すC言語コードも参考にする。