author: hjjdebug
date: 2025年 06月 25日 星期三 15:35:26 CST
descrip: glib-object 中G_DEFINE_TYPE 宏都作了什么?
文章目錄
- 1. 測試代碼
- 2 給出它的展開式.
- 3.說說它都生成了什么?
- 3.1. my_foo_get_type() 函數
- 3.2. static GType my_foo_get_type_once(void)
- 3.3. my_foo_class_intern_init
- 3.4. my_foo_get_instance_private
- 4 小結:
講多了也說不明白,就給一個簡單的例子. 事實勝于雄辯
1. 測試代碼
$ cat main.c
#include <glib-object.h>
// 用c 語言來完成類的繼承
//假如想創建一個MyObject 類, 繼承GObject 基類
//框架, 寫兩個類
struct _Foo {GObject obj1;int value;
};struct _FooClass {GObjectClass class1;
};
//定義兩個小名
typedef struct _Foo Foo;
typedef struct _FooClass FooClass;//關鍵代碼來了(一個復雜的宏),
//前面定義了2個類型Foo,FooClass,my_foo是名稱前綴
G_DEFINE_TYPE(Foo, my_foo, G_TYPE_OBJECT)static void my_foo_init(Foo *self) {self->value = 0;
}
static void my_foo_class_init(FooClass *klass) {(void) klass;
}int main(void)
{return 0;
}
2 給出它的展開式.
本來我們一步一步把宏如何展開的給出來, 但是太復雜了. 直接給出G_DEFINE_TYPE展開的結果了.
其中G_TYPE_OBJECT 是一個整數 , 其值是 20<<2
$ cat readme.c
static void my_foo_init(Foo* self);
static void my_foo_class_init(FooClass* klass);
static GType my_foo_get_type_once(void);
static gpointer my_foo_parent_class = ((void*)0);static gint Foo_private_offset;
static void my_foo_class_intern_init(gpointer klass)
{my_foo_parent_class = g_type_class_peek_parent(klass);if (Foo_private_offset != 0)g_type_class_adjust_private_offset(klass, &Foo_private_offset);my_foo_class_init((FooClass*)klass);
} __attribute__((__unused__))static inline gpointer my_foo_get_instance_private(Foo* self)
{return (((gpointer)((guint8*)(self) + (glong)(Foo_private_offset))));
}
GType my_foo_get_type(void)
{static GType static_g_define_type_id = 0;if ((__extension__({ //斷言static_g_define_type_id 就是 指針大小(64位機8字節)_Static_assert(sizeof *(&static_g_define_type_id) == sizeof(gpointer), "Expression evaluates to false");(void) (0 ? (gpointer) * (&static_g_define_type_id) : ((void *)0)); //這一行就是0(!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; //__typeof__是GNUC 擴展表達式__typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5); gapg_temp_newval; })) && g_once_init_enter_pointer (&static_g_define_type_id)); }))){GType g_define_type_id = my_foo_get_type_once();(__extension__({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer ((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id)); }));}return static_g_define_type_id;
} __attribute__((__noinline__))
static GType my_foo_get_type_once(void)
{GType g_define_type_id = g_type_register_static_simple(((GType)((20) << (2))), g_intern_static_string("Foo"), sizeof(FooClass), (GClassInitFunc)(void (*)(void))my_foo_class_intern_init, sizeof(Foo), (GInstanceInitFunc)(void (*)(void))my_foo_init, (GTypeFlags)0);{{{};}}return g_define_type_id;
}
3.說說它都生成了什么?
它生成了4個函數, my_foo_get_type,my_foo_get_type_once 用來獲取類型id.
my_foo_class_intern_init, 類初始化
my_foo_get_instance_private 實例初始化
另外還定義了一個Foo_private_offset 變量
3.1. my_foo_get_type() 函數
extension 是是C語言中的一個編譯器指令,
用于告訴編譯器在宏定義中有一些語法擴展,可能不符合 ANSI 標準的語法,
不要動不動就被我警告或報錯. 例如 __typeof__就是一種擴展語法
typeof (*(&static_g_define_type_id)) gapg_temp_newval;
__typeof__是GNUC 擴展表達式, 用以獲取后邊表達式的類型.
*(&static_g_define_type_id)通過取地址后解引用,本質上等價于直接訪問static_g_define_type_id
該句就是定義一個變量gapg_temp_newval, 類型與static_g_define_type_id 一樣
typeof ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id);
定義gapg_temp_atomic = &static_g_define_type_id;
__atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5);
向gapg_temp_newval 原子加載數值, 5代表內存模型,__ATOMIC_SEQ_CST(順序一致性模型)
前邊為真時,執行后邊語句:g_once_init_enter_pointer (&static_g_define_type_id));
這是一個宏.
gboolean g_once_init_enter_pointer (void *location);
實際上就是取static_g_define_type_id 的值之意. 如果也為真,取反為假,則不執行大括號語句
但第一次static_g_define_type_id的值為0,取反后if條件為真,則執行大括號語句.
調用my_foo_get_type_once ,返回類型id->g_define_type_id,賦值給static_g_define_type_id
則以后再調該函數,因static_g_define_type_id 已經為真,不走if塊語句,直接返回static_g_define_type_id
3.2. static GType my_foo_get_type_once(void)
向系統注冊類. 返回定義的類型id g_define_type_id
注冊函數原型:
GType g_type_register_static_simple (GType parent_type, //20<<2
const gchar *type_name, //Foo
guint class_size, // 類的大小,sizeof(FooClass)
GClassInitFunc class_init, // my_foo_class_intern_init
guint instance_size, // sizeof(Foo)
GInstanceInitFunc instance_init, // my_foo_init,
GTypeFlags flags); // 0
gpointer 就是void *, GType 就是uint64_t;
3.3. my_foo_class_intern_init
該函數會調用用戶定義的my_foo_class_init
3.4. my_foo_get_instance_private
該函數很簡單, 調整類指針指向私有地址
4 小結:
G_DEFINE_TYPE宏在GObject系統中自動生成了類型注冊相關代碼。
它會生成4個函數:
my_foo_get_type()用于獲取類型ID,
my_foo_get_type_once()執行實際的類型注冊,
my_foo_class_intern_init()處理類初始化,
my_foo_get_instance_private()管理實例私有數據。
關鍵是通過g_type_register_static_simple()注冊新類型,指定父類型(G_TYPE_OBJECT)、
類大小、實例大小和初始化函數
該機制通過靜態變量實現線程安全的單次初始化,為GLib的對象系統提供了類型注冊的基礎設施.
是c語言面向對象編程的實現。