4. クラスのヘッダを記述する
C++ オブジェクトのクラス Stack に対応する構造体を定義し、必要なマクロをヘッダ・ファイル sample-stack.h の中に定義する。ここで参考にするのは、ヘッダ・コードでお決まりの部分にあるサンプルコード。これをテンプレートにして記述するのが簡単。
まず通常のC言語のヘッダ・ファイル同様に:
#include SAMPLE_STACK_H #define SAMPLE_STACK_H
で始めて、次で終わる:
#endif /* SAMPLE_STACK_H */
次に、クラスを定義するのに必要な他のヘッダを取り込むわけだけども、GObject による実装で必須なのは、次のヘッダ・ファイル:
#include <glib-object.h>
型管理システムやシグナル管理システム、プロパティ管理システムなどを提供するクラス構造体 GObject を継承するために必要なファイル。
そしてクラス構造体とインスタンス構造体を定義するわけだけども、これらの構造体をクラスぽく扱えるようにするためのマクロを六つ定義しておく。お約束ごとに従うだけ:
#define SAMPLE_TYPE_STACK (sample_stack_get_type ()) #define SAMPLE_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SAMPLE_TYPE_STACK, SampleStack)) #define SAMPLE_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SAMPLE_TYPE_STACK, SampleStackClass)) #define SAMPLE_IS_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SAMPLE_TYPE_STACK)) #define SAMPLE_IS_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SAMPLE_TYPE_STACK)) #define SAMPLE_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SAMPLE_TYPE_STACK, SampleStackClass))
テンプレートを参考にアプリケーション名とクラス名に置き換えるだけ。作成したクラスをコンパイルするときに妙なコンパイル・エラーが発生するなら、このマクロを確認する。たとえば、ホワイト・スペースが必要な場所に正しく挿入しているか、アプリケーション名とクラス名を逆にしていないかなど確認する。まぁ、こんなマクロを記述するくらいなら、C++ で記述した方がマシと思うかもしれないけど。
次に、これから定義する二つの構造体の型マクロを定義しておく:
typedef struct _SampleStack SampleStack; typedef struct _SampleStackClass SampleStackClass; typedef struct _SampleStackPrivate SampleStackPrivate;
上がインスタンス構造体で、下がクラス構造体。一個余計な型マクロがあるけれども、これは Private なメンバを定義する構造体のためのもの。このヘッダの中では定義せず、クラスのソース・コードで定義することに注意する。
で、いよいよインスタンス構造体とクラス構造体を定義する。ここで説明されているとおり、インスタンス構造体は GObject parent、クラス構造体は GObjectClass parent; が必須でお決まりのコード。ちなみに、これらのメンバは共に Private な扱いになる:
struct _SampleStack { /*< private >*/ GObject parent;
SampleStackPrivate *_private; };
struct _SampleStackClass { /*< private >*/ GObjectClass object_class;
/* Class Members */ void (*initStack) (SampleStack *self); void (*push) (SampleStack *self, const char c); char (*pop) (SampleStack *self); };
とりあえず、クラスのメソッドは無視することにして、重要なポイントが一つ。メソッドを除く Private なメンバはクラス構造体ではなく、インスタンス構造体に定義するということ。なるべくコメント /*< private >*/ を付与して分かりやすくする。Private なメソッドはクラスの初期化/終了処理で記述するので。このあたりは ここの後半部分で説明されている。
ふm。このあたりから C++ のオブジェクト指向的な考えがぶっ飛んでしまうところだけども、詳細はこのあたりで説明されている。
最後に、クラス構造体の中でメソッドを定義する:
struct _SampleStackClass { /*< private >*/ GObjectClass object_class;
/* Class Members */ void (*initStack) (SampleStack *self); void (*push) (SampleStack *self, const char c); char (*pop) (SampleStack *self); };
クラスのメソッドは単なる関数へのポインタ。ポイントはインスタンス SampleClass *self を第一引数にとること。実装はクラスのソースコードに記述する。三つのメソッドが全て Public なのでプロトタイプ宣言することも忘れずに。関数名や他の引数は C++ のクラス Stack のメソッド同様である。そしてヘッダの作成の締めに、クラス SampleStackClass を型システムへ登録する関数 GType sample_stack_get_type (void); も宣言しておく。その実装はクラスのソース・コードで:
GType sample_stack_get_type (void);
void sample_stack_initStack (SampleStack *self); void sample_stack_push (SampleStack *self, const char c); char sample_stack_pop (SampleStack *self);
実際にメソッドを呼び出す際は、名前衝突を避けるためにアプリケーション名とクラス名を指定し、sample_stack_pop() というスタイルで呼び出すことになるので、ここで宣言するメソッドの関数にも sample_stack_ を付与してエキスポートしていることに注意。
なんとかクラスぽい形になっているけど、やはり GObject のメカニズムが足かせになっているのか、C++ 的なクラスの姿は全くない。
クラス SampleStack を型管理システムへ登録する部分、Private なメンバの定義と、これを用いたメソッドの実装については、また明日にでも。