GObject調用父類函數

最近在分析Gstreamer的代碼時,發現GstPipeline中有以下代碼:html

result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

但搜索當前文件並無發現有對parent_class變量的定義,查詢後發現這是GObject在相應宏展開時所定義的一個靜態變量,當使用G_DEFINE_TYPE宏定義一個GObject對象時,宏會自動展開生成相應的代碼,子類能夠經過parent_class調用父類的函數。下面對這種使用方式作一個簡單分析。git

 

強制轉換

GObject的介紹中提到,父類的結構體必須做爲子類結構體的第一個成員,因爲C語言中定義第一個結構體數據必須存放在所分配內存的起始位,因此這樣能夠經過快速強制轉換訪問父類成員,這也是C語言裏的一個重要的技巧,示例以下:github

 1 struct _GTypeClass{
 2   GType g_type;
 3 };
 4 struct _GTypeInstance{
 5   GTypeClass *g_class;
 6 };
 7 /* A definitions */
 8 typedef struct {
 9   GTypeInstance parent;
10   int field_a;
11   int field_b;
12 } A;
13 typedef struct {
14   GTypeClass parent_class;
15   void (*method_a) (void);
16   void (*method_b) (void);
17 } AClass;
18 
19 /* B definitions. */
20 typedef struct {
21   A parent;
22   int field_c;
23   int field_d;
24 } B;
25 typedef struct {
26   AClass parent_class;
27   void (*method_c) (void);
28   void (*method_d) (void);
29 } BClass;
30 
31 B *b;
32 b->parent.parent.g_class->g_type //普通順序訪問
33 ((GTypeInstance *) b)->g_class->g_type //快速強制轉換

 一樣在GstPipeline的聲明中,父類一樣是做爲子類的第一個結構體成員:ide

 1 #define GST_TYPE_PIPELINE               (gst_pipeline_get_type ())
 2 
 3 struct _GstPipeline {
 4   GstBin         bin;
 5   ...
 6 };
 7 
 8 struct _GstPipelineClass {
 9   GstBinClass parent_class;
10   ...
11 };

 

G_DEFINE_TYPE宏

爲了能讓GObject在new出一個對象的時候順利找到相應類型,必須將自定義的類添加到類型系統裏面,GObject提供了一系列的 G_DEFINE_TYPE 宏來快速定義一個新的對象類型,GstPipeline則經過下列宏定義對象:函數

1 G_DEFINE_TYPE_WITH_CODE (GstPipeline, gst_pipeline, GST_TYPE_BIN, _do_init);

 咱們能夠經過GCC的預處理來展開宏,查看生成的代碼:spa

1 $ cd gstreamer-1.14.4
2 $ gcc -E `pkg-config --cflags glib-2.0` -I. gst/gstpipeline.c > gstpipeline_preposs.c
3 $ clang-format -i gstpipeline_preposs.c  

宏展開後,主要生成一個父類class對象的指針(parent_class),class對象的內部初始化接口(gst_pipeline_class_intern_init)獲取惟一標識當前類型的接口(gst_pipeline_get_type),parent_class也會在class init的時候進行初始化:指針

 1 static void gst_pipeline_init(GstPipeline *self);
 2 static void gst_pipeline_class_init(GstPipelineClass *klass);
 3 static gpointer parent_class = ((void *)0) ;
 4 
 5 // init parent_class pointer to GstBinClass 
 6 // and call user defined class init function.
 7 static void gst_pipeline_class_intern_init(gpointer klass) {
 8   parent_class = g_type_class_peek_parent(klass);
 9   gst_pipeline_class_init((GstPipelineClass *)klass);
10 }
11 
12 // generate unique id for each class
13 // and register instance and class init functions
14 GType gst_pipeline_get_type(void) {
15   static volatile gsize g_define_type_id__volatile = 0;
16   if ( g_once_init_enter(&g_define_type_id__volatile)) {
17     GType g_define_type_id = g_type_register_static_simple(
18       (gst_bin_get_type()),                   /* GST_TYPE_BIN  parent type*/
19       g_intern_static_string("GstPipeline"),  /* type name */
20       sizeof(GstPipelineClass),               /* class size */
21       (GClassInitFunc)gst_pipeline_class_intern_init, /* class init */ 
22       sizeof(GstPipeline),                            /* instance size */ 
23       (GInstanceInitFunc)gst_pipeline_init,           /* instance init */
24       (GTypeFlags)0);                                 /* flags */
25      ...
26       g_once_init_leave((&g_define_type_id__volatile), (gsize)(g_define_type_id));
27     }
28     return g_define_type_id__volatile;
29 }

 

建立GObject對象

建立GObject對象須要經過g_object_new(),這個接口的第一個函數就是須要建立對象的類型,好比咱們能夠經過以下方式建立GstPipeline對象:code

1 GstPipeline *pipeline = g_object_new (GST_TYPE_PIPELINE, "name", "pipeline1", NULL);

這裏的GST_TYPE_PIPELINE是GstPipeline的類型,這個宏被定義爲gst_pipeline_get_type(),這個函數經過上面提到的宏展開獲得。orm

當對象被建立時,咱們定義的類對象(GstPipelineClass)初始化函數gst_pipeline_class_init() 被調用,而後對象實例(GstPipeline)初始化函數被調用 gst_pipeline_init()。htm

這裏之因此須要持有父類class對象的指針,是由於當子類在執行class init時,能夠覆蓋父類的方法,若是將子類class對象強制轉換爲父類class對象,調用的方法任然是子類重寫了的方法,因此這裏必須持有父類class對象的指針(parent_class),才能夠調用父類的方法。

 

在子類中調用父類函數

在子類中調用父類方法,咱們只需將parent_class轉換爲父類class對象,經過函數指針調用便可。例如GstPipeline中獲取時鐘的方法:

1 GST_ELEMENT_CLASS (parent_class)->provide_clock (GST_ELEMENT(pipeline));

因爲GstPipeline的父類是GstBin,GstBin的父類是GstElement,在GstBin的class init函數gst_bin_class_init中,provide_clock被初始化爲 gst_bin_provide_clock_func,在子類GstPipeline的class init函數gst_pipeline_class_init中,provide_clock被初始化爲gst_pipeline_provide_clock_func。

所以在GstPipeline中就能夠經過parent_class調用GstBin中的接口。

 

這套機制只能保證子類能夠調用父類的方法,可能能顯示調用父類以上基類的接口,例如上例中,若是GstElement中也有一個provide_clock的實現,則在GstPipeline中沒法直接調用GstElement中的實現。

 

遺留問題

在G_DEFINE_TYPE宏的實現中,parent_class還帶有一個前綴,以下:

1 #define _G_DEFINE_TYPE_EXTENDED_BEGIN_PRE(TypeName, type_name, TYPE_PARENT) \
2 ...\
3 static gpointer type_name##_parent_class = NULL; \
4 ...

但實際展開的代碼爲:

1 static gpointer parent_class = ((void *)0) ;

爲何這裏會去掉type_name還不得而知。

 20190517更新:

由於在G_DEFINE_TYPE前,將gst_pipeline_parent_class定義爲一個宏,因此G_DEFINE_TYPE展開後,及爲parent_class.

1 #define gst_pipeline_parent_class parent_class
2 G_DEFINE_TYPE_WITH_CODE (GstStream, gst_stream, GST_TYPE_OBJECT, _do_init);

 

引用

https://github.com/GNOME/glib/blob/master/gobject/gtype.h
https://developer.gnome.org/gobject/stable/gobject-Type-Information.htm
https://developer.gnome.org/gobject/stable/gobject-The-Base-Object-Type.html
https://developer.gnome.org/gobject/stable/gtype-instantiable-classed.html

 

做者: John.Leng
本文版權歸做者全部,歡迎轉載。商業轉載請聯繫做者得到受權,非商業轉載請在文章頁面明顯位置給出原文鏈接.
相關文章
相關標籤/搜索