GObject:基本

GObjectは、純粋なCのオブジェクト指向拡張機能を実装するGLibライブラリの一部です。 GLib自体に加えて、同様の概念がGStreamer、GSettings、ATK、Pangoなどのプロジェクト全体およびGNOMEプロジェクト全体で使用されるほか、多数のアプリケーションアプリケーション(GIMP、Inkscape、Geany、Geditなど)でも使用されます。 PythonやJavaなどのメインストリーム言語から、HaskellやDなどの改良版で終わる多数のプログラミング言語には、GLib / GTK +へのバインディングがあり、かなりの数の言語では、GTK +へのバインディングが一般的にGUIを構築する唯一の方法です。



他の同様のプロジェクトとは異なり、GObjectはアーキテクチャ機能によって区別されます。その目的は、純粋なCおよびGObjectを使用して記述されたライブラリバインディングを、動的型付けやガベージコレクターを使用したメモリ管理など、簡単かつ透過的に実装することです。 これは、プログラマーがGObject APIを理解し始めるときに感じるかもしれない過度の複雑さを説明しています。 それにもかかわらず、このシステムは非常に考え抜かれており、論理的であるため、C ++またはJavaに精通しているプログラマーは、以下に述べるすべてを理解する上で問題を抱えることはないはずです。



この記事では、GLibオブジェクトシステムタイプの操作の基本を説明します。

画像



GObjectに関するサイクル全体:



GObject:基本

GObject:継承とインターフェース

GObject:カプセル化、インスタンス化、イントロスペクション



GObject型システムの最も基本的なレベルはGTypeシステムで、プログラマが操作できるすべての可能な型の実行時記述を実装します。 このシステムは、動的GLibタイプシステムの使用に基づいて構築されたライブラリへのバインディングを持つ他の言語のコードとCコードを接続する一種の「接着剤」として機能します。



GObject型システムは、単一継承(およびJavaに似たインターフェースの概念、およびより複雑な概念ソリューションを考慮)、カプセル化、仮想関数、C ++フィールドに似たいわゆるプロパティをサポートしますが、多くの追加機能とイベント指向のパラダイムのフレームワーク内で効果的で開発された設計を作成できる信号システム。



GObject型システムは、gchar(純粋なCのcharの類似体)、gpointer(void型のポインターの類似体)、gbooleanなどの基本的なアンマネージ型で構成されます。 これらの型をオーバーライドする主な目的は、統合と移植性です。 インスタンス化可能な分類型。C++クラスに広く似ています。 インスタンス化不可能な分類型— Javaインターフェースまたは純粋に抽象化されたC ++クラスに類似したインターフェース。



GObjectライブラリの操作を示す簡単な例を作成してみましょう。 最初に、GLibを使用している開発者の間で採用されている主要な規則を理解しましょう。 オブジェクトの名前には、一般的な名前、つまり一種の名前空間と種があります。 たとえば、GLibライブラリに存在するすべてのオブジェクトには「G」というプレフィックスが付き、GTK +オブジェクトには「Gtk」というプレフィックスが付きます。 オブジェクトの名前は「ラクダ」表記で記述され、これらのオブジェクトに関連する関数メソッドの名前は「蛇」で表記されます。 マクロは伝統的に大文字で記述され、単語はアンダースコアで区切られます。 たとえば、AnimalCatはAnimal名前空間に属するCatオブジェクトであり、その「メソッド」はanimal_cat_say_meow()のようになります。



新しいcatオブジェクトを記述する2つのファイル、animalcat.cとanimalcat.hを作成しましょう。 ヘッダーファイルに繰り返し含まれないように保護を追加し、GObjectライブラリを接続します。



#ifndef _ANIMAL_CAT_H_ #define _ANIMAL_CAT_H_ #include <glib-object.h>
      
      





C ++コンパイラとの互換性と再有効化に対する厳密な保護のためにGLibで使用される2つの従来のマクロを追加します。



 G_BEGIN_DECLS /*       */ G_END_DECLS #endif /* _ANIMAL_CAT_H_ */
      
      





ヘッダーファイルに必要な2つの最も重要な定義を追加します。 最初のマクロの形式は次のとおりです。



 #define OURNAMESPACE_TYPE_OUROBJECT ournamespace_ourobject_get_type()
      
      





つまり、特定のケースでは:



 #define ANIMAL_TYPE_CAT animal_cat_get_type()
      
      





この関数は、型に関するすべての基本情報を含むGType構造体を返します。名前、オブジェクトに必要なメモリ量、クラスとオブジェクトの初期化および終了関数へのリンク(C ++コンストラクターとデストラクターのアナログ)など。



2番目のマクロは一般的に次のようになります。



 G_DECARE_DERIVABLE_TYPE (NamespaceObject, namespace_object, NAMESPACE, OBJECT, ParentClass)
      
      





そして私たちの場合:



 G_DECLARE_DERIVABLE_TYPE (AnimalCat, animal_cat, ANIMAL, CAT, GObject)
      
      





このマクロ定義は、型変換、特定の型のメンバーシップのチェックなどを実行する重要なマクロのセット全体に分解されます。最後の引数では、GObjectは親クラスとして宣言され、そこからGObject型システムのすべてのオブジェクトが継承されます。



実際、それから継承されたGObjectまたはオブジェクトは、_NamespaceObjectと_NamespaceObjectClassの2つの構造で構成されています。



 struct _AnimalCat struct _AnimalCatClass
      
      





それらを実際のオブジェクトおよびクラスと呼ぶことに同意します。 GObjectの現在のバージョンでは、一般に、オブジェクトの構造を明示的な形式で実装する必要はありません;多くの場合、マクロを展開した結果として自動的に生成されます。 仮想関数を再定義して継承階層を構築することが目標である場合、クラスを明示的に実装する必要があります。



GObjectには2つのタイプがあり、継承の可能性が異なります-派生可能と最終です。 2番目の場合、最後のマクロはG_DECLARE_FINAL_TYPEのようになり、_NamespaceObject構造体は.cファイルで明示的に宣言する必要があります。 派生物の場合、この構造を宣言する必要はありません;マクロが展開するときに自動的に生成されます。



_AnimalCatClassの構造について説明します。 この構造は単一のコピーに存在し、施設の最初のインスタンスの作成中に作成され、最後のインスタンスの破棄後に破棄されます。 最初のフィールドとして、このクラスから直接継承するため、親クラス(この場合はGObject)と同じ構造を含む必要があります。 この後、他のフィールド、主に仮想メソッドの機能を実装する関数へのポインター、およびC ++クラスの静的フィールドに類似したフィールドがあります。



たとえば、_AnimalCatClassクラスは次のようになります。



 struct _AnimalCatClass { GObjectClass parent_class; void (*say_meow)(AnimalCat*); };
      
      





特定のメソッドの宣言でヘッダーファイルを補足します。 GObjectから継承されたオブジェクトは、最も単純な形式では次のようなコードによって作成されます。



 g_object_new(NAMESPACE_TYPE_OBJECT, NULL);
      
      





次の形式の関数で同様のコードをラップするのが慣例です。



 AnimalCat* animal_cat_new();
      
      





特定のメソッドの宣言でヘッダーファイルを終了します。



 void animal_cat_say_meow(AnimalCat* self);
      
      





ご覧のとおり、C ++などの言語とは異なり、この場合、特定のインスタンスに明示的にポインターを渡す必要があります。



したがって、ヘッダーファイルは次のようになります。



 #ifndef _ANIMAL_CAT_H_ #define _ANIMAL_CAT_H_ #include <glib-object.h> G_BEGIN_DECLS #define ANIMAL_TYPE_CAT animal_cat_get_type() G_DECLARE_DERIVABLE_TYPE (AnimalCat, animal_cat, ANIMAL, CAT, GObject) struct _AnimalCatClass { GObjectClass parent_class; void (*say_meow) (AnimalCat*); }; AnimalCat* animal_cat_new(); void animal_cat_say_meow(AnimalCat* self); G_END_DECLS #endif /* _ANIMAL_CAT_H_ */
      
      





ソースコード自体を含むファイルに移りましょう。 サンプルを鳴らすために必要なstdio.hと同様に、ヘッダーファイルを含めます。



 #include <stdio.h> #include "animalcat.h"
      
      





もう1つの重要なマクロの時間です。この時間は次のようになります。

 G_DEFINE_TYPE (NamespaceObject, namespace_object, G_TYPE_OBJECT)
      
      





マクロに渡す最後の引数は、親オブジェクトに関連する、ヘッダーファイルの先頭で定義したものに似ています。 catobjectの場合、ANIMAL_TYPE_CATのように見えますが、この場合、親オブジェクトG_TYPE_OBJECTと同様のマクロを使用します。 状況に適用すると、この行は次のようになります。



 G_DEFINE_TYPE (AnimalCat, animal_cat, G_TYPE_OBJECT)
      
      





少し前に、derivableとfinal-gobjectの違いについて話したことを覚えていますか? 継承の可能性を示唆しない最終オブジェクトを定義した場合、構造を定義する必要があります



 struct _NamespaceObject { ParentObject parent; };
      
      





少なくとも1つのフィールド(および最初のフィールド)を含む必要があります-親オブジェクトと同様の構造。 このような必須要件は、親オブジェクトの構造へのポインターのアドレスが、それから継承されたすべてのオブジェクトの構造へのポインターのアドレスと同一でなければならないという事実に関連しています。



この場合、構造は少なくとも次のようになります。



 struct _AnimalCat { GObject parent; };
      
      





もう一度お知らせします-final型のGObjectを使用する場合にのみ、この構造を明示的に定義する必要があります。この例では、この定義を省略しています。



実行可能コードに直接進みます。



animal_cat_say_meow()関数を定義して、仮想関数のメカニズムがGObjectでどのように機能するかを示します(少し後で、これが必要な理由がわかります)。



 static void animal_cat_real_say_meow(AnimalCat* self) { printf("Cat say: MEOW!\n"); }
      
      





また、ヘッダーファイルで宣言された実際に呼び出されるメソッドを定義します。



 void animal_cat_say_meow(AnimalCat* self) { AnimalCatClass* klass = ANIMAL_CAT_GET_CLASS (self); klass->say_meow(self); }
      
      





次の2つの重要な関数が自動的に呼び出されます-最初はこのオブジェクトの最初のインスタンスを作成するとき、2番目は特定のインスタンスを作成するとき、つまりC ++コンストラクターの類似物です。



 static void animal_cat_class_init(AnimalCatClass* self) { printf("First instance of AnimalCat was created\n"); self->say_meow = animal_cat_real_say_meow; }
      
      





ご覧のとおり、オブジェクトの構造クラスで定義された「仮想」関数を再定義しました。



上記のコードはどうなりますか? animal_cat_class_init()関数では、オブジェクトのクラス構造を初期化するときに、say_meow関数の参照にanimal_cat_real_say_meow()関数のアドレスが割り当てられます。そのアドレスは現在、AnimalCatClass構​​造体のsay_meowフィールドに割り当てられています。 後続オブジェクトでは、class_initで終わる名前の対応する関数でこの動作をオーバーライドできます。



「コンストラクター」に進みましょう。



 static void animal_cat_init(AnimalCat* self) { printf("Kitty was born.\n"); }
      
      





AnimalCatオブジェクトの新しいインスタンスへのポインターを返すラッパー関数を作成しましょう。



 AnimalCat* animal_cat_new() { return g_object_new(ANIMAL_TYPE_CAT, NULL); }
      
      





コードの正常性をテストするための単純なmain()関数を作成します。



 #include «animalcat.h" int main(int argc, char** argv) { AnimalCat* cat_a = animal_cat_new(); AnimalCat* cat_b = animal_cat_new(); animal_cat_say_meow(cat_a); return 0; }
      
      





およびmakefile:



 CFLAGS = -Wall -g `pkg-config --cflags glib-2.0 gobject-2.0` LDFLAGS = `pkg-config --libs glib-2.0 gobject-2.0` EXEC = kitty SRC = main.c animalcat.c animalcat.c OBJ = main.o animalcat.o animalcat.o $(EXEC): $(OBJ) $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS) %.o: %.c $(CC) -c -o $@ $< $(CFLAGS) .PHONY: clean clean: rm -f $(OBJ) $(EXEC)
      
      





収集して実行します:



 make ./kitty
      
      





 First instance of AnimalCat was created. Kitty was born. Kitty was born. Cat say: MEOW!
      
      






All Articles