1. gobject 类型系统和注册过程
1. 手动注册:
#include <glib-object.h>#define M_TYPE_DATA (m_data_get_type())typedef struct _MData MData;
struct _MData {GObject parent;double value;
};typedef struct _MDataClass MDataClass;
struct _MDataClass {GObjectClass parent_class;
};static void
m_data_class_init (MDataClass *class)
{
}static void
m_data_init (MData *d)
{
}GType
m_data_get_type (void)
{static GType type = 0;GTypeInfo info;if (type == 0) {info.class_size = sizeof (MDataClass);info.base_init = NULL;info.base_finalize = NULL;info.class_init = (GClassInitFunc) m_data_class_init;info.class_finalize = NULL;info.class_data = NULL;info.instance_size = sizeof (MData);info.n_preallocs = 0;info.instance_init = (GInstanceInitFunc) m_data_init;info.value_table = NULL;type = g_type_register_static (G_TYPE_OBJECT, "MData", &info, 0);}return type;
}int
main (int argc, char **argv)
{GType dtype;MData *d;dtype = m_data_get_type();if (dtype)g_print ("Registration was a success. The type is %lx.\\n", dtype);elseg_print ("Registration failed.\\n");d = g_object_new (M_TYPE_DATA, NULL);if (d)g_print ("Instantiation was a success. The instance address is %p.\\n", d);elseg_print ("Instantiation failed.\\n");return 0;
}
2. G_DEFINE_TYPEZ宏注册
下面是用G_DEFINE_TYPE
宏来自动注册,自动注册需要遵循命名约定。
G_DEFINE_TYPE
执行以下操作:
- 声明类初始化函数
<name space>_<name>_class_init
。(你需要定义它)- 例如,如果对象名称是
MData
,则为m_data_class_init
。
- 例如,如果对象名称是
- 声明实例初始化函数
<name space>_<name>_init
。(你需要定义它)- 例如,如果对象名称是
MData
,则为m_data_init()
。
- 例如,如果对象名称是
-
- 定义一个指向父亲的静态变量
<name space>_<name>_parent_class
。 - 例如,如果对象名称是
MData
,则它是m_data_parent_class
- 定义一个指向父亲的静态变量
- 定义一个
<name space>_<name>_get_type ()
函数- 例如,如果对象名称是
MData
,则它是m_data_get_type
- 注册是在这个函数中完成的,就像上面手动注册的一样。
- 例如,如果对象名称是
#include <glib-object.h>#define M_TYPE_DATA (m_data_get_type())
typedef struct _MData MData;
struct _MData {GObject parent;double value;
};typedef struct _MDataClass MDataClass;
struct _MDataClass {GObjectClass parent_class;
};G_DEFINE_TYPE (MData, m_data, G_TYPE_OBJECT)static void
m_data_class_init (MDataClass *class)
{
}static void
m_data_init (MData *d)
{
}int
main (int argc, char **argv)
{GType dtype;MData *d;dtype = m_data_get_type();if (dtype)g_print ("Registration was a success. The type is %lx.\\n", dtype);elseg_print ("Registration failed.\\n");d = g_object_new (M_TYPE_DATA, NULL);if (d)g_print ("Instantiation was a success. The instance address is %p.\\n", d);elseg_print ("Instantiation failed.\\n");return 0;
}
3. G_DECLARE_FINAL_TYPE和G_DECLARE_DERIVABLE_TYPE
如果要定义可派生类型对象,请改用G_DECLARE_DERIVABLE_TYPE
。但是,在大多数情况下,您可能会编写最终类型对象。
首先,你需要手动定义宏:
#define M_TYPE_DATA (m_data_get_type ())
这应该在G_DECLARE_FINAL_TYPE
之前完成。
然后,
G_DECLARE_FINAL_TYPE
执行以下操作:
- 声明
<name space>_<name>_get_type ()
。(需要定义,你可以用G_DEFINE_TYPE
来定义) - 对象的
typedef
定义。- 例如,对象名称是
MData
,则typedef struct _MData MData
。你需要在G_DEFINE_TYPE之前
定义struct _MData
。
- 例如,对象名称是
<NAME SPACE>_<NAME>
宏。- 例如对象是
MData
,则宏是M_DATA
,它会扩展程一个函数,用于指针转换。 - 例如,
M_DATA(obj)
将obj
的类型转换为MData
。
- 例如对象是
<NAME SPACE>_IS_<NAME>
宏。- 例如,对象名称是
MData
,则宏是M_IS_DATA
。它将扩展为一个函数,检查参数是否是MData
类型。 - 如果参数指向
MData
及其后继,则返回true
。
- 例如,对象名称是
- 定义了类结构,final类不需要有自己的类结构成员。(但是仍然要定义类结构初始化函数)
#include <glib-object.h>#define M_TYPE_DATA (m_data_get_type())G_DECLARE_FINAL_TYPE (MData, m_data, M, DATA, GObject)struct _MData {GObject parent;double value;
};G_DEFINE_TYPE (MData, m_data, G_TYPE_OBJECT)static void
m_data_class_init (MDataClass *class)
{ }static void
m_data_init (MData *d)
{ }int
main (int argc, char **argv)
{{GType type = m_data_get_type();if (type != 0)g_print ("Registration was a success. The type is %lx.\\n", type);elseg_print ("Registration failed.\\n");}{MData *data = g_object_new (M_TYPE_DATA, NULL);if (data != NULL)g_print ("Instantiation was a success. The instance address is %p.\\n", data);elseg_print ("Instantiation failed.\\n");if (M_IS_DATA (data))g_print ("data is MData instance.\\n");elseg_print ("data is not MData instance.\\n");if (G_IS_OBJECT (data))g_print ("data is GOBject instance.\\n");elseg_print ("data is not GObjec instance.\\n");}return 0;
}
4. 将文件拆分
将内容拆分成三个文件:
- main.c
- mdata.h
- mdata.c
头文件的内容是公开的,即对任何文件开放。
头文件包括:
- 提供类型信息(如
M_TYPE_DATA
宏等)。 - 类型转换(如
M_DATA()
,G_OBJECT()
) - 类型检查(如
M_IS_DATA()
,G_IS_OBJECT()
) - 以及公共函数的宏。
mdata.h:
#ifndef MDATA_H
#define MDATA_H#include <glib-object.h>#define M_TYPE_DATA (m_data_get_type())
G_DECLARE_FINAL_TYPE (MData, m_data, M, DATA, GObject)gboolean
m_data_get_value (MData *data, double *value);void
m_data_set_value (MData *data, double value);MData *
m_data_new (double value);#endif // MDATA_H
mdata.c:
#include "mdata.h"
#include <stdbool.h>struct _MData {GObject base;double value;
};G_DEFINE_TYPE (MData, m_data, G_TYPE_OBJECT)static void
m_data_class_init (MDataClass *d)
{ }static void
m_data_init (MData *d)
{ }gboolean
m_data_get_value (MData *data, double *value)
{g_return_val_if_fail (M_IS_DATA (data), false);*value = data->value;return true;
}void
m_data_set_value (MData *data, double value)
{g_return_if_fail (M_IS_DATA (data));data->value = value;
}MData *
m_data_new (double value)
{MData *data = g_object_new (M_TYPE_DATA, NULL);data->value = value;return data;
}
main.c:
#include "mdata.h"#include <glib-object.h>int
main (void)
{MData *data = m_data_new (10.0);double value;if (m_data_get_value (data, &value))g_print ("value : %lf\\n", value);elseg_print ("m_data_get_value() failed.\\n");m_data_set_value (data, -20.0);g_print ("Now, set data with %lf.\\n", -20.0);if (m_data_get_value (data, &value))g_print ("value : %lf.\\n", value);elseg_print ("m_data_get_value failed.\\n");return 0;
}
看下面一个例子:
MData *
m_data_add (MData *self, MData *other)
{g_return_val_if_fail (M_IS_DATA (self), NULL);g_return_val_if_fail (M_IS_DATA (other), NULL);double value;if (!m_data_get_value (other, &value))return NULL;return m_data_new (self->value + value);
}
函数对外是公开的,就像面向对象中的方法,但是不能用other->value
来获取值,而是m_double_get_value
。
通常,对象的结构不对其他对象开放。
当一个对象A访问对象B时,A必须使用B提供的公共函数。
参考资料
有关更多公约惯例的信息,参阅GObject API 参考 - 约定