大家來學GObject

  • 895 views
Uploaded on

 

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
895
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
25
Comments
0
Likes
0

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. 頁首在這裡 大家來學GObjectRevision 2傅書煜 (shuyufu at gmail.com)頁尾在這裡
  • 2. 頁首在這裡 為什麼會有這份文件? GObject是什麼? 學會GObject有什麼用? 哪裡取得/ GObject? 學習GObject前必需俱備的知識 結構(Struct) 指標(Pointer) 函式指標(Function Pointer) 在沒有GObject的情況下 GObject基礎課程 GObject 公有實體結構變數和私有實體結構變數 存取資料成員 公有成員函式和私有成員函式,(純)虛擬函式和預設實作 存取父類別結構 抽象類別 介面 GObject進階課程 類別的建構與解構 訊號 GObject 什麼是沒有在這份文件提到的 vtable Closure 工具 glib-genmarshal GObject Generator 參考資料頁尾在這裡
  • 3. 頁首在這裡為什麼會有這份文件?基於工作上的需要,我必需瞭解如何使用GObject,在學習的過程中,我發現正體中文資料較少[1],簡中倒有一些.在對GObject有一些瞭解後,我覺得GObject是不錯的框架,所以,我決定要寫一份正體中文的教學文件,讓更多人瞭解它。文件內容主要是來自於[2]再加上個人經驗撰寫,如果有錯誤或建議請告訴我以進行修正。頁尾在這裡
  • 4. 頁首在這裡GObject是什麼?GObject是開源專案GLib中的一部份,它用C語言實作出物件導向的框架。這份文件是基於GObject ReferenceManual 2.26.1[2]製作的,你可以在[2]觀看線上版或下載在本機上瀏覽。學會GObject有什麼用?有許多開源專案是基於GObject開發的,例如:GIO、GTK+、GStreamer等。學會GObject後再看這些開源專案的源碼或進行開發會更容易。同時也有輔助性的函式庫,比如json-glib[3],它可以將一個GObject序列化成JSON的格式.哪裡取得/安裝GObject?你可以到GTK+的網站下載到你想要GLib的源碼。頁尾在這裡
  • 5. 頁首在這裡學習GObject前必需俱備的知識我們假設讀者對C語言有一定程度的瞭解,如果你對C語言不熟的話,請務必先複習以下主題。結構(Struct)結構是變數的集合體,可以放置許多的變數。指標(Pointer)指標是一個變數,和一般變數不一樣的是它所儲存的是一個位址。函式指標(Function Pointer)一般指標所指向的位址是資料,而函式指標所指向的位址則是函式。在沒有GObject的情況下先開始看GObject之前,試想如果要你用C語言實作類別,你會怎麼實做資料成員(Data Member)和成員函式(Member Function)?頁尾在這裡
  • 6. 頁首在這裡GObject基礎課程在基礎課程裡,我們將藉由實作一個簡單的GObject來介紹我們在類別裡常用的物件變數、類別變數和(abstract)method如何宣告和定義。第一個GObject我們先來實作一個最簡單的GObject吧! 1. /* maman-bar.h */ 2. #ifndef __MAMAN_BAR_H__ 3. #define __MAMAN_BAR_H__ 4. 5. #include <glib-object.h> 6. 7. G_BEGIN_DECLS 8. 9. #define MAMAN_TYPE_BAR (maman_bar_get_type ()) 10. #define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) 11. #define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) 12. #define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR)) 13. #define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) 14. #define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) 15. 16. typedef struct _MamanBar MamanBar; 17. typedef struct _MamanBarClass MamanBarClass; 18. 19. struct _MamanBar { 20. GObject parent_instance; 21. /* instance members */ 22. }; 23. 24. struct _MamanBarClass { 25. GObjectClass parent_class; 26. /* class members */ 27. }; 28. 29. GType maman_bar_get_type (void); 30. MamanBar * maman_bar_new (void); 31. 32. G_END_DECLS 33. 34. #endif /* __MAMAN_BAR_H__ */ 35. 36. /* maman-bar.c */ 37. #include “maman-bar.h” 38. 39. /* will create maman_bar_get_type and set maman_bar_parent_class */ 40. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); 41. 42. static void maman_bar_class_init (MamanBarClass *klass) { 43. GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 44. } 45. 46. static void maman_bar_init (MamanBar *self) { 47. /* initialize the object */ 48. } 49. 50. MamanBar * maman_bar_new (void) { 51. return MAMAN_BAR (g_object_new (MAMAN_TYPE_BAR, NULL)); 52. }這是一個僅實作出最基本架構的GObject。在GObject裡,實體結構(_MamanBar;第19~22行)和類別結構(_MamanBarClass;第24~27行)是分開宣告的。實體結構是每產生一個實體就有一份,而類別結構則是被所有實體共享.這邊要注意的是實體(類別)結構裡的第一個變數必須是父類別的實體(類別)結構(第20、頁尾在這裡
  • 7. 頁首在這裡25行),接下來才是自己想宣告的變數。glib-object.h是每個GObject都必需include的標頭標(第5行),而一開始的巨集是用來取得類別ID(第9行)、替實體(類別)結構轉型(第10~11行)、檢查指標所指的位置是否為某個類別的實體結構(第12行)或類別結構(第13行),和從實體結構取得對應的類別結構(第14行).這些巨集我們會在之後的例子看到它們的用處.G_DEFINE_TYPE是一個幫助我們快速定義一個GObject的巨集(第70行),第一個參數是將類別名稱以Camel case的方式帶入,第二個參數是將類別名稱以小寫並以底線符號做區隔帶入,第三個則是父類別的類別ID.我們將巨集展開後會發現其中定義一個指向父類別結構的指標和定義取得ID的函式,也就是第29行的maman_bar_get_type,這也是為什麼我們僅在標頭檔宣告而沒實作卻又可以呼叫的原因.在取得ID的函式中會自動引用以第二個參考為前置符號並以_class_init和_init的兩個函式._class_init是類別結構的初始化函式只有在產生第一個實體之前會被呼叫,而_init則是實體初始化函式,因為每產生一個實體都會呼叫一次.慣例上,為了方便產生GObject,還會提供一個產生GObject的函式(第30行宣告,第50~52行實作).因此第51行是在產生一個MamanBar的實體,但,g_object_new回傳的型態為gpointer,所以使用MAMAN_BAR巨集將指標的型別轉為MamanBar*.最後,我們該怎麼操作MamanBar這個GObject呢? 1. #include “maman-bar.h” 2. 3. int main (void) { 4. MamanBar *bar = NULL; 5. MamanBarClass *bar_class = NULL; 6. g_type_init (); 7. bar = maman_bar_new (); 8. bar_class = MAMAN_BAR_GET_CLASS (bar); 9. g_object_unref (bar); 10. return 0; 11. }第6行的g_type_init ()是使用任何一個GObject前都必需呼叫的函式,否則會產生錯誤.GObject使用參考計數[?]作為記憶體管理策略,換句話說,不會有malloc或free這類的函式,取而代之的是g_object_ref和g_object_unref.當第7行剛透過maman_bar_new產生實體結構時,參考計數為1(事實上是g_object_new產生實體時會將參考計數設為1),最後main函式結束前用g_object_unref把參考計劃減1,歸零後GObject會自行解構/回收記憶體.公有實體結構變數和私有實體結構變數要宣告公有資料成員很簡單,只要在實體結構內宣告即可.而私有則較為麻煩,有兩種做法,第一種是道德性的勸說,在宣告實體結構時一併加上註解,告訴使用的人不應該存取這些變數.另一種則是真正的隱藏.以下列出程式碼,並將新增的部份加上紅色. 1. /* maman-bar.h */ 2. #ifndef __MAMAN_BAR_H__ 3. #define __MAMAN_BAR_H__ 4. 5. #include <glib-object.h> 6. 7. G_BEGIN_DECLS 8. 9. #define MAMAN_TYPE_BAR (maman_bar_get_type ()) 10. #define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))頁尾在這裡
  • 8. 頁首在這裡 11. #define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) 12. #define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR)) 13. #define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) 14. #define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) 15. 16. typedef struct _MamanBar MamanBar; 17. typedef struct _MamanBarClass MamanBarClass; 18. typedef struct _MamanBarPrivate MamanBarPrivate; 19. 20. struct _MamanBar { 21. GObject parent_instance; 22. /* instance members */ 23. gint public_count; 24. /*< private >*/ 25. gint private_count; 26. /*< private >*/ 27. MamanBarPrivate *priv; 28. }; 29. 30. struct _MamanBarClass { 31. GObjectClass parent_class; 32. /* class members */ 33. }; 34. 35. GType maman_bar_get_type (void); 36. MamanBar * maman_bar_new (void); 37. 38. G_END_DECLS 39. 40. #endif /* __MAMAN_BAR_H__ */ 41. 42. /* maman-bar.c */ 43. #include “maman-bar.h” 44. 45. /* will create maman_bar_get_type and set maman_bar_parent_class */ 46. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); 47. 48. #define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate)) 49. 50. struct _MamanBarPrivate { 51. gint a; 52. gint b; 53. }; 54. 55. static void maman_bar_class_init (MamanBarClass *klass) { 56. GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 57. g_type_class_add_private (klass, sizeof (MamanBarPrivate)); 58. } 59. 60. static void maman_bar_init (MamanBar *self) { 61. /* initialize the object */ 62. self->priv = MAMAN_BAR_GET_PRIVATE (self); 63. } 64. 65. MamanBar * maman_bar_new (void) { 66. return MAMAN_BAR (g_object_new (MAMAN_TYPE_BAR, NULL)); 67. }第23行的public_count就是屬於公有實體結構變數,而第25行的private_count則是勸說型的私有實體結構變數,所以我們在第24行加上註解/*< private >*/告訴使用的人不應該存取這些變數,這兩種實體結構變數只要使用MamanBar的人include標頭檔就很清楚struct的宣告時的內容,都無法防止外部直接存取.而確實隱藏的方法是在.c檔內宣告私有結構(第50~53行)並在maman_bar_class_init時將私有結構加入類別裡(第57行),之後要存取時則是透過第48行宣告的巨集將私有結構的位置取出來,像第66行做的事.這邊要注意的是,在實體結構內不一定要有一個指標指向私有結構(第27行)的位置,這例子中只是為了示範(其實也可以做為快取,之後就不用再透過巨集取得位置),所以在實體結構內宣告了一個私有結構的指標.存取資料成員頁尾在這裡
  • 9. 頁首在這裡當有了結構變數後接下來就是如何存取它們?當然,最簡單的就是直接用->的方式存取,不過,這不僅有違封裝性,也不是我們要講的方法.我們要介紹的是如何利用GObject提供的介面替一個GObject安裝(install)屬性(property)並使用特定的函式存取它們.但,為什麼要這麼麻煩?直接用->就好了啊.事實上,GObject對屬性的存取可以提供一定的規範,以及當搭配其它以GObject為基礎的函式庫時就會明顯感受到它的作用,不需要額外寫code就可以發揮作用.如json-glib可以將一個GObject序列化為JSON格式,但,它僅能作用在安裝好的屬性上. 1. /* maman-bar.h */ 2. #ifndef __MAMAN_BAR_H__ 3. #define __MAMAN_BAR_H__ 4. 5. #include <glib-object.h> 6. 7. G_BEGIN_DECLS 8. 9. #define MAMAN_TYPE_BAR (maman_bar_get_type ()) 10. #define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) 11. #define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) 12. #define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR)) 13. #define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) 14. #define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) 15. 16. typedef struct _MamanBar MamanBar; 17. typedef struct _MamanBarClass MamanBarClass; 18. typedef struct _MamanBarPrivate MamanBarPrivate; 19. 20. struct _MamanBar { 21. GObject parent_instance; 22. /* instance members */ 23. gint public_count; 24. /*< private >*/ 25. gint private_count; 26. /*< private >*/ 27. MamanBarPrivate *priv; 28. }; 29. 30. struct _MamanBarClass { 31. GObjectClass parent_class; 32. /* class members */ 33. }; 34. 35. GType maman_bar_get_type (void); 36. MamanBar * maman_bar_new (void); 37. 38. G_END_DECLS 39. 40. #endif /* __MAMAN_BAR_H__ */ 41. 42. /* maman-bar.c */ 43. #include “maman-bar.h” 44. 45. /* will create maman_bar_get_type and set maman_bar_parent_class */ 46. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); 47. 48. #define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate)) 49. 50. #define DEFAULT_PRIV_COUNT 0 51. 52. struct _MamanBarPrivate { 53. gint a; 54. gint b; 55. }; 56. 57. static void maman_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { 58. MamanBar *bar = MAMAN_BAR (object); 59. switch (property_id) { 60. case PROP_PRIV_COUNT: 61. g_value_set_int (value, bar->private_count); 62. break; 63. default: 64. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);頁尾在這裡
  • 10. 頁首在這裡 65. } 66. } 67. 68. static void maman_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { 69. MamanBar *bar = MAMAN_BAR (object); 70. switch (property_id) { 71. case PROP_PRIV_COUNT: 72. bar->private_count = g_value_get_int (value); 73. break; 74. default: 75. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 76. } 77. } 78. 79. static void maman_bar_class_init (MamanBarClass *klass) { 80. GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 81. gobject_class->get_property = maman_bar_get_property; 82. gobject_class->set_property = maman_bar_set_property; 83. g_object_class_install_property ( 84. gobject_class, 85. PROP_PRIV_COUNT, 86. g_param_spec_int ( 87. “priv-count”, 88. “MamanBar private count prop”, 89. “MamanBar’s private count”, 90. G_MININT, 91. G_MAXINT, 92. DEFAULT_PRIV_COUNT, 93. G_PARAM_READWRITE 94. ) 95. ); 96. g_type_class_add_private (klass, sizeof (MamanBarPrivate)); 97. } 98. 99. static void maman_bar_init (MamanBar *self) { 100. /* initialize the object */ 101. self->priv = MAMAN_BAR_GET_PRIVATE (self); 102. } 103. 104. MamanBar * maman_bar_new (void) { 105. return MAMAN_BAR (g_object_new (MAMAN_TYPE_BAR, NULL)); 106. }GObject要安裝(install)屬性(property)(第80~93行)之前,必需先覆寫GObjectClass的get_property和set_property兩個函式指標(第79~80行),否則會發生錯誤.在這例子中我們安裝了priv-count這個屬性的ID為PROP_PRIV_COUNT,其型別為int,可接受的範圍介於G_MININT和G_MAXINT之間,預設值為DEFAULT_PRIV_COUNT並且可以READ-WRITE.之後透過GObject所提供的函式存取屬性時就會呼叫到maman_bar_get_property和maman_bar_set_property並將需要的資訊一併傳入.而在get_property和set_property兩個函式中只要用switch敘述判斷property_id就可以知道要用什麼函式存取GValue了.GValue在GObject中扮演一個泛型(generic)的容器(contaier),當承載的資料為原始資料型態,我們不需要特別做什麼,但,當承載的資料為自訂的型別時,就需要提供一些函式了[???],GValue我們會在[???]做一次介紹,目前僅需要知道GValue是一個泛型的容器就好.最後,我們來看要怎麼存取MamanBar的屬性. 1. #include "maman-bar.h" 2. 3. int main (void) { 4. MamanBar *bar = NULL; 5. MamanBarClass *bar_class = NULL; 6. gint count = 0; 7. GValue val = {0, }; 8. 9. g_type_init (); 10. 11. bar = maman_bar_new (); 12. 13. g_value_init (&val, G_TYPE_INT);頁尾在這裡
  • 11. 頁首在這裡 14. g_value_set_int (&val, count); 15. 16. g_object_set_property (G_OBJECT (bar), “priv-count”, &val); 17. g_object_get_property (G_OBJECT (bar), “priv_count”, &val); 18. count = g_value_get_int (&val); 19. 20. g_object_set (G_OBJECT (bar), “priv_count”, count, NULL); 21. g_object_get (G_OBJECT (bar), “priv-count”, &count, NULL); 22. 23. g_object_unref (bar); 24. 25. return 0; 26. }我們可以透過g_object_set_property和g_object_get_property一次存取一個屬性(第16~17行),或者透過g_object_set和g_object_get一次存取多個屬性(第20~21行).當使用g_object_set_property和g_object_get_property,必需以GValue作為媒介將值封裝起來或從其中將值取出.而g_object_set和g_object_get則不然,直接將要存取的屬性名和儲存位置以一對的方式傳入,並允許多對,最後以NULL結束,也可以達到同樣的效果.公有成員函式和私有成員函式,(純)虛擬函式和預設實作成員函式的作法是在類別結構內宣告函式指標,然後在_class_init裡覆寫函式指標.而公有/私有成員函式兩者在GObject實作上很像,只差在於能不能直接呼叫到而已,事實上,在其它的OO實作上,不也是這樣嗎? 1. /* maman-bar.h */ 2. #ifndef __MAMAN_BAR_H__ 3. #define __MAMAN_BAR_H__ 4. 5. #include <glib-object.h> 6. 7. G_BEGIN_DECLS 8. 9. #define MAMAN_TYPE_BAR (maman_bar_get_type ()) 10. #define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) 11. #define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) 12. #define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR)) 13. #define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) 14. #define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) 15. 16. typedef struct _MamanBar MamanBar; 17. typedef struct _MamanBarClass MamanBarClass; 18. typedef struct _MamanBarPrivate MamanBarPrivate; 19. 20. struct _MamanBar { 21. GObject parent_instance; 22. /* instance members */ 23. gint public_count; 24. /*< private >*/ 25. gint private_count; 26. /*< private >*/ 27. MamanBarPrivate *priv; 28. }; 29. 30. struct _MamanBarClass { 31. GObjectClass parent_class; 32. /* class members */ 33. void (*public_func) (MamanBar *); 34. void (*priv_func) (MamanBar *); 35. }; 36. 37. GType maman_bar_get_type (void); 38. MamanBar * maman_bar_new (void); 39. void maman_bar_public_func (MamanBar *self); 40. void maman_bar_other_func (MamanBar *self); 41. 42. G_END_DECLS 43.頁尾在這裡
  • 12. 頁首在這裡 44. #endif /* __MAMAN_BAR_H__ */ 45. 46. /* maman-bar.c */ 47. #include “maman-bar.h” 48. 49. /* will create maman_bar_get_type and set maman_bar_parent_class */ 50. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); 51. 52. #define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate)) 53. 54. #define DEFAULT_PRIV_COUNT 0 55. 56. struct _MamanBarPrivate { 57. gint a; 58. gint b; 59. }; 60. 61. static void maman_bar_public_func_default (MamanBar *self) { 62. } 63. 64. static void maman_bar_priv_func_default (MamanBar *self) { 65. } 66. 67. static void maman_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { 68. MamanBar *bar = MAMAN_BAR (object); 69. switch (property_id) { 70. case PROP_PRIV_COUNT: 71. g_value_set_int (value, bar->private_count); 72. break; 73. default: 74. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 75. } 76. } 77. 78. static void maman_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { 79. MamanBar *bar = MAMAN_BAR (object); 80. switch (property_id) { 81. case PROP_PRIV_COUNT: 82. bar->private_count = g_value_get_int (value); 83. break; 84. default: 85. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 86. } 87. } 88. 89. static void maman_bar_class_init (MamanBarClass *klass) { 90. GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 91. gobject_class->get_property = maman_bar_get_property; 92. gobject_class->set_property = maman_bar_set_property; 93. klass->public_func = maman_bar_public_func_default; 94. klass->priv_func = maman_bar_priv_func_default; 95. g_object_class_install_property ( 96. gobject_class, 97. PROP_PRIV_COUNT, 98. g_param_spec_int ( 99. “priv-count”, 100. “MamanBar private count prop”, 101. “MamanBar’s private count”, 102. G_MININT, 103. G_MAXINT, 104. DEFAULT_PRIV_COUNT, 105. G_PARAM_READWRITE 106. ) 107. ); 108. g_type_class_add_private (klass, sizeof (MamanBarPrivate)); 109. } 110. 111. static void maman_bar_init (MamanBar *self) { 112. /* initialize the object */ 113. self->priv = MAMAN_BAR_GET_PRIVATE (self); 114. } 115. 116. MamanBar * maman_bar_new (void) { 117. return MAMAN_BAR (g_object_new (MAMAN_TYPE_BAR, NULL)); 118. } 119.頁尾在這裡
  • 13. 頁首在這裡 120. void maman_bar_public_func (MamanBar *self) { 121. g_return_if_fail (MAMAN_IS_BAR (self)); 122. MAMAN_BAR_GET_CLASS (self)->public_func (self); 123. } 124. 125. void maman_bar_other_func (MamanBar *self) { 126. g_return_if_fail (MAMAN_IS_BAR (self)); 127. /* pre-priv-func code here */ 128. MAMAN_BAR_GET_CLASS (self)->priv_func (self); 129. /* post-priv-func code here */ 130. }在標題頭裡提供maman_bar_public_func和maman_bar_other_func兩函式(宣告在第39~40行),用以存取類別結構裡的public_func和priv_func函式指標.在_class_init裡,我們將public_func和priv_func函式指標分別指向maman_bar_public_func_default和maman_bar_priv_func_default(第93~94行).在maman_bar_public_func裡(第120~123行),我們僅檢查傳入的指標是否確實指向一個MamanBar的實體,之後便呼叫類別結構裡的public_func函式指標,這就是公有成員函式.而在maman_bar_other_func裡(第125~130行),我們除了檢查傳入的指標以外,更在呼叫priv_func這個私有成員的前後加入一些其它的處理邏輯.存取父類別結構存取父類別結構讓我們得以呼叫父類別的成員函式,哪該怎麼實作呢?這並不難,只要將某個類別結構傳入g_type_class_peek_parent這個函式就會回傳一個父類別結構的指標, 之後就能透過這指標存取父類別結構.還記得之前提到的G_DEFINE_TYPE嗎?這個巨集幫我們做的其中一件事就是宣告一個父類別結構的變數並指向父類別結構,而這變數的名字是以第G_DEFINE_TYPE的第二個變數開頭並以_parent_class結尾. 1. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT);在這例子裡,則是maman_bar_parent_class.抽象類別有了之前的基礎,要宣告一個抽象類別更是簡單了,只要把G_DEFINE_TYPE換成G_DEFINE_ABSTRACT_TYPE就可以了.介面介面宣告的方式和類別大同小異,差在於介面是繼承自GTypeInterface而不是GObjectClass. 1. /* maman-ibaz.h */ 2. #ifndef __MAMAN_IBAZ_H__ 3. #define __MAMAN_IBAZ_H__ 4. 5. #include <glib-object.h> 6. 7. G_BEGIN_DECLS 8. 9. #define MAMAN_IBAZ_TYPE (maman_ibaz_get_type ()) 10. #define MAMAN_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_IBAZ_TYPE, MamanIbaz) ) 11. #define MAMAN_IS_IBAZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_IBAZ_TYPE)) 12. #define MAMAN_IBAZ_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), MAMAN_IBAZ_TYPE,頁尾在這裡
  • 14. 頁首在這裡 MamanIbazInterface)) 13. 14. typedef struct _MamanIbaz MamanIbaz; /* dummy object */ 15. typedef struct _MamanIbazInterface MamanIbazInterface; 16. 17. struct _MamanIbazInterface { 18. GTypeInterface parent; 19. 20. void (*do_action) (MamanIbaz *self); 21. }; 22. 23. GType maman_ibaz_get_type (void); 24. 25. void maman_ibaz_do_action (MamanIbaz *self); 26. 27. G_END_DECLS 28. 29. #endif /* __MAMAN_IBAZ_H__ */ 30. 31. /* maman-ibaz.c */ 32. #include “maman-ibaz.h” 33. 34. G_DEFINE_INTERFACE (MamanIbaz, maman_ibaz, G_TYPE_INVALID) 35. 36. static void maman_ibaz_default_init (MamanIbazInterface *klass) 37. { 38. } 39. 40. void maman_ibaz_do_action (MamanIbaz *self) 41. { 42. g_return_if_fail (MAMAN_IS_IBAZ (self)); 43. 44. MAMAN_IBAZ_GET_INTERFACE (self)->do_action (self); 45. }jjjj頁尾在這裡
  • 15. 頁首在這裡GObject進階課程在進階課程裡,我們會瞭解GObject建構和解構的順序,和GObject中的訊號.類別的建構與解構在[2]裡有兩張表在解釋GObjec和GObject Interfacet的建構和解構.我們這邊簡單的在這邊再出現一次.GObject的解構和解構Invocation time Function Invoked Functions parametersFirst call types base_init function On the inheritance tree oftog_type_create_instancefor classes from fundamentaltarget type type to target type. base_init is invoked once for each class structure. target types class_init function On target types class structure interface initialization, see the section called “Interface Initialization”Each call target types instance_init On objects instancetog_type_create_instancefor functiontarget typeLast call interface destruction, seetog_type_free_instance for the section called “Interfacetarget type Destruction” target types class_finalize On target types class structure function types base_finalize function On the inheritance tree of classes from fundamental type to target type. base_finalize is invoked once for each class structure.GObject Interface的建構Invocation time Function Invoked Functions parameters RemarkFirst call interface base_init On interface vtable Register interface頁尾在這裡
  • 16. 頁首在這裡tog_type_create_ function signals here (use ainstance for type local static booleanimplementing interface variable as described above to make sure not to register them twice.). interface interface_init On interface vtable Initialize interface function implementation. That is, initialize the interface method pointers in the interface structure to the functions implementation.GObject Interface的解構Invocation time Function Invoked Functions parametersLast call to interface interface_finalize On interface vtableg_type_free_instance for type functionimplementing interface interface base_finalize function On interface vtable訊號在GObject提供了訊號(Signal)這個機制,讓物件操作者可以透過callback函式得知物件狀態改變了,更簡單的說,訊號是GObject中的觀察者模式(Observer Pattern).GObject必需在class_init函式裡使用g_signal_new註冊要提供外界觀察的訊號,之後物件操作者才能註冊callback函式.首先我們在類別結構裡宣告預設的訊號處理函式和一個變數儲存訊號的ID(第27~28行),然後在class_init函式裡先指定訊號處理函式的位置(第100行)然後再註冊一個訊號(第115~127行),比較特殊的是,除了訊號處理函式,我們還額外註冊了一個accumulator函式(第120行),緊接著每一次的訊號calback函式之後accumulator函式就會接著被呼叫一次. 1. /* maman-bar.h */ 2. #ifndef __MAMAN_BAR_H__ 3. #define __MAMAN_BAR_H__ 4. #include <glib-object.h> 5. G_BEGIN_DECLS 6. #define MAMAN_TYPE_BAR (maman_bar_get_type ()) 7. #define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar)) 8. #define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass)) 9. #define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))頁尾在這裡
  • 17. 頁首在這裡 10. #define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR)) 11. #define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass)) 12. typedef struct _MamanBar MamanBar; 13. typedef struct _MamanBarClass MamanBarClass; 14. typedef struct _MamanBarPrivate MamanBarPrivate; 15. struct _MamanBar { 16. GObject parent_instance; 17. /* instance members */ 18. gint public_count; 19. /*< private >*/ 20. gint private_count; 21. /*< private >*/ 22. MamanBarPrivate *priv; 23. }; 24. struct _MamanBarClass { 25. GObjectClass parent_class; 26. /* class members */ 27. guint my_signal_id; 28. gint (*my_signal_handler) (GObject *, gint, gpointer, gpointer); 29. void (*public_func) (MamanBar *); 30. void (*priv_func) (MamanBar *); 31. }; 32. GType maman_bar_get_type (void); 33. MamanBar * maman_bar_new (void); 34. void maman_bar_public_func (MamanBar *self); 35. void maman_bar_other_func (MamanBar *self); 36. G_END_DECLS 37. #endif /* __MAMAN_BAR_H__ */ 38. /* maman-bar.c */ 39. #include “maman-bar.h” 40. #include “mamanmarshal.h” 41. 42. /* will create maman_bar_get_type and set maman_bar_parent_class */ 43. G_DEFINE_TYPE (MamanBar, maman_bar, G_TYPE_OBJECT); 44. #define MAMAN_BAR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MAMAN_TYPE_BAR, MamanBarPrivate)) 45. #define DEFAULT_PRIV_COUNT 0 46. struct _MamanBarPrivate { 47. gint a; 48. gint b; 49. }; 50. static void maman_bar_public_func_default (MamanBar *self) { 51. } 52. static void maman_bar_priv_func_default (MamanBar *self) { 53. } 54. static void maman_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { 55. MamanBar *bar = MAMAN_BAR (object); 56. switch (property_id) { 57. case PROP_PRIV_COUNT: 58. g_value_set_int (value, bar->private_count); 59. break; 60. default: 61. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 62. } 63. } 64. static void maman_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { 65. MamanBar *bar = MAMAN_BAR (object);頁尾在這裡
  • 18. 頁首在這裡 66. switch (property_id) { 67. case PROP_PRIV_COUNT: 68. bar->private_count = g_value_get_int (value); 69. break; 70. default: 71. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); 72. } 73. } 74. 75. static gboolean maman_bar_my_signal_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer data) 76. { 77. static gint ret = 0; 78. 79. g_print ("%s : %dn", __FUNCTION__, g_value_get_int (handler_return)); 80. 81. ret += g_value_get_int (handler_return); 82. 83. g_value_set_int (return_accu, ret); 84. 85. return TRUE; 86. } 87. 88. static gint maman_bar_my_signal_handler (GObject *obj, gint i, gpointer data, gpointer user_data) 89. { 90. /* Here, we trigger the real rotate. */ 91. g_print ("%s on %pn", __FUNCTION__, obj); 92. 93. return 2; 94. } 95. 96. static void maman_bar_class_init (MamanBarClass *klass) { 97. GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 98. gobject_class->get_property = maman_bar_get_property; 99. gobject_class->set_property = maman_bar_set_property; 100. klass->public_func = maman_bar_public_func_default; 101. klass->priv_func = maman_bar_priv_func_default; 102. klass->my_signal_handler = maman_bar_my_signal_handler; 103. g_object_class_install_property ( 104. gobject_class, 105. PROP_PRIV_COUNT, 106. g_param_spec_int ( 107. “priv-count”, 108. “MamanBar private count prop”, 109. “MamanBar’s private count”, 110. G_MININT, 111. G_MAXINT, 112. DEFAULT_PRIV_COUNT, 113. G_PARAM_READWRITE 114. ) 115. ); 116. 117. klass->my_signal_id = g_signal_new ( 118. "mysignal", 119. G_TYPE_FROM_CLASS (klass), 120. G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED, 121. G_STRUCT_OFFSET (MamanBarClass, my_signal_handler), /* class_offset */ 122. maman_bar_my_signal_accumulator, /* accumulator */頁尾在這裡
  • 19. 頁首在這裡 123. NULL, /* accu_data */ 124. maman_cclosure_marshal_INT__INT_POINTER, /* g_signal_newv */ 125. G_TYPE_INT, /* return_type */ 126. 2, /* n_params */ 127. G_TYPE_INT, /* param_types */ 128. G_TYPE_POINTER 129. ); 130. 131. g_type_class_add_private (klass, sizeof (MamanBarPrivate)); 132. } 133. static void maman_bar_init (MamanBar *self) { 134. /* initialize the object */ 135. self->priv = MAMAN_BAR_GET_PRIVATE (self); 136. } 137. MamanBar * maman_bar_new (void) { 138. return MAMAN_BAR (g_object_new (MAMAN_TYPE_BAR, NULL)); 139. } 140. void maman_bar_public_func (MamanBar *self) { 141. g_return_if_fail (MAMAN_IS_BAR (self)); 142. MAMAN_BAR_GET_CLASS (self)->public_func (self); 143. } 144. void maman_bar_other_func (MamanBar *self) { 145. g_return_if_fail (MAMAN_IS_BAR (self)); 146. /* pre-priv-func code here */ 147. MAMAN_BAR_GET_CLASS (self)->priv_func (self); 148. /* post-priv-func code here */ 149. }物件操作者便可以使用訊號得知物件的狀態改變.加快你的GObject在編譯GObject時,只要定義G_DISABLE_CHECKS、G_DISABLE_CAST_CHECKS和G_DISABLE_ASSERT,我們就可以將MAMAN_IS_BAR和MAMAN_BAR這類的巨集變的簡單(單純的casting)達成加速的目的.頁尾在這裡
  • 20. 頁首在這裡什麼是沒有在這份文件提到的vtable這不是C++的vtableClosureclosure是GObject實作callback的抽象層.頁尾在這裡
  • 21. 頁首在這裡工具glib-genmarshal在註冊一個GObject的訊號時,預設的marshaler僅針對常用的型態,當預設的marshaler無法滿足需求時,可以用glib-genmarshal幫你產生一個.GObject Generator在[4]的工具只要輸入一些基本的資訊就可以幫你產生一個GObject最基本需要的源碼.頁尾在這裡
  • 22. 頁首在這裡參考資料[1] gobject-learning[2] GObject Reference Manual (2.26.1)[3] JSON-GLib[4] GObject Generator頁尾在這裡