1.1 类头文件
#ifndef PM_DLIST_H
#define PM_DLIST_H
#include <glib-object.h>
#define PM_TYPE_DLIST (pm_dlist_get_type ())
/* 将基类指针类型转换为PMDList类的指针类型 */
#define PM_DLIST(object) G_TYPE_CHECK_INSTANCE_CAST ((object),\
PM_TYPE_DLIST, PMDList)
typedef struct _PMDList PMDList;
struct _PMDList {
GObject parent_instance;
};
typedef struct _PMDListClass PMDListClass;
struct _PMDListClass {
GObjectClass parent_class;
};
GType pm_dlist_get_type (void);
#endif
分析
这章节类头文件唯一改动的地方是加了PM_DLIST(object)宏。该宏的作用是将基类指针类型转换为PMDList类的指针类型。
下文还会遇见G_OBJECT宏,G_OBJECT_CLASS宏。子类指针转向GObject父类指针。
我的感悟
参考博客作者认为头文件越简洁越好,C文件去实现头文件简洁背后的繁琐(丑陋)。
1.2 类函数
#include"pm-dlist.h"
/**
* 此宏的三个功能
* 1.函数GTyep pm_dlist_get_type()的具体实现,由该宏自动实现
* 2.注册函数前缀pm_dlish到GObject
* 3.注册实例结构体名称PMDList到GObject
*/
G_DEFINE_TYPE(PMDList, pm_dlist, G_TYPE_OBJECT);
/* 获得私有成员变量结构体地址 */
#define PM_DLIST_GET_PRIVATE(obj) \
G_TYPE_INSTANCE_GET_PRIVATE ((obj), PM_TYPE_DLIST, PMDListPrivate)
typedef struct _PMDListPrivate PMDListPrivate;
struct _PMDListPrivate {
PMDList *head;
PMDList *tail;
};
/* 属性对应的ID,真正的属性ID不能为0 */
enum PropertyDlist{
PROPERTY_DLIST_0,/*占位*/
PROPERTY_DLIST_HEAD,
PROPERTY_DLIST_TAIL
};
static void
pm_dlist_set_property(GObject *object, guint property_id,
const GValue *value, GParamSpec *pspec){
/*基类转换成子类指针*/
PMDList *self = PM_DLIST(object);
/*通过宏获取唯一私有属性地址*/
PMDListPrivate *priv = PM_DLIST_GET_PRIVATE(self);
switch (property_id){
case PROPERTY_DLIST_HEAD:
priv->head = g_value_get_pointer (value);
break;
case PROPERTY_DLIST_TAIL:
priv->tail = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void
pm_dlist_get_property (GObject *object, guint property_id,
GValue *value, GParamSpec *pspec)
{
PMDList *self = PM_DLIST (object);
PMDListPrivate *priv = PM_DLIST_GET_PRIVATE (self);
switch (property_id) {
case PROPERTY_DLIST_HEAD:
g_value_set_pointer (value, priv->head);
break;
case PROPERTY_DLIST_TAIL:
g_value_set_pointer (value, priv->tail);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
pm_dlist_class_init(PMDListClass *klass)
{
/*私有成员添加到类里面*/
g_type_class_add_private(klass, sizeof(PMDListPrivate));
GObjectClass *base_class = G_OBJECT_CLASS(klass);
base_class->set_property = pm_dlist_set_property;
base_class->get_property = pm_dlist_get_property;
GParamSpec *pspec;
pspec = g_param_spec_pointer ("head",
"Head node",
"The head node of the double list",
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
g_object_class_install_property (base_class, PROPERTY_DLIST_HEAD, pspec);
pspec = g_param_spec_pointer ("tail",
"Tail node",
"The tail node of the double list",
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
g_object_class_install_property (base_class, PROPERTY_DLIST_TAIL, pspec);
}
static void
pm_dlist_init (PMDList *self)
{
}
分析
这一部分新增函数目的就是,外部可以查看和设置私有成员变量。其实这一部分,也可以执行某些函数,不一定只查看或者设置变量。
1.enum PropertyDlist 枚举先为属性编号。
2.编写pm_dlist_set_property和pm_dlist_get_property函数
3.父类留有的set_property和get_property指针指向第二步编写的函数。
4.g_object_class_install_property函数注册属性到子类。
g_param_spec_pointer ("head",
"Head node",
"The head node of the double list",
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);
g_param_spec_pointer可以将 ” 属性名:属性值 “参数打包为GParamSpec类型变量。其中Param 1:键名,Param 2:键名的昵称,Param 3:对这个键值对的详细描述,Param 4:用于表示键值的访问权限,G_PARAM_READABLE | G_PARAM_WRITABLE 是指定属性即可读又可写,G_PARAM_CONSTRUCT 是设定属性可以在对象实例化之时(g_object_new)被设置。
g_object_class_install_property (base_class, \
PROPERTY_DLIST_TAIL, pspec);
g_object_class_install_property 函数用于将 GParamSpec 类型变量所包含的数据插入到 GObject 子类中,其中的细节可以忽略,只需要知道该函数的第一个参数为 GObject 子类的类结构体,第二个参数是 GParamSpec 对应的属性 ID。GObject 子类的属性 ID 在前文已经提及,它是 GObject 子类设计者定义的宏或枚举类型。第三个参数是要安装值向 GObject 子类中的 GParamSpec 类型的变量指针。
一定要注意,g_object_class_install_property 函数的第二个参数值不能为 0。
1.3 主函数
#include "pm-dlist.h"
int
main (void)
{
GValue list1_tail = {0};
GValue list2_head = {0};
/* GObject 库的类型管理系统的初始化 */
g_type_init ();
PMDList *list1, *list2;
gpointer *head, *tail;
list1 = g_object_new (PM_TYPE_DLIST,
"head", NULL,
"tail", NULL,
NULL);
list2 = g_object_new (PM_TYPE_DLIST,
"head", NULL,
"tail", NULL,
NULL);
g_value_init(&list1_tail, G_TYPE_POINTER);
g_value_init(&list2_head, G_TYPE_POINTER);
g_value_set_pointer(&list1_tail, list1);
g_value_set_pointer(&list2_head, list2);
g_object_set_property(G_OBJECT(list2), "head", &list1_tail);
g_object_set_property(G_OBJECT(list1), "tail", &list2_head);
g_object_get(list1, "head", &head, "tail", &tail, NULL);
g_printf("%p\n", head);
g_printf("%p\n", tail);
g_object_set(list1, "head", 0x22, "tail", 0x33, NULL);
g_object_get(list1, "head", &head, "tail", &tail, NULL);
//g_printf("%p\n", g_value_get_pointer(&list1_tail));
g_printf("%p\n", head);
g_printf("%p\n", tail);
g_value_unset(&list1_tail);
g_value_unset(&list2_head);
g_object_unref (list1);
g_object_unref (list2);
return 0;
}
分析
- g_object_set_property和g_object_get_property。参数是GObject类和GValue容器。一次只能设置和查询一次属性。
- g_object_set和g_object_get。参数是gpointer指针和属性对应变量类型即可。可同时查看和设置多个属性,最后NULL结尾。
感悟
DeeepStream项目中是不是可以修改和设置属性。从而实现不同码流的设置,sink属性的开关???
GValue
在C语言里,通用类型我们一般是用void *以表示任何类型的数据。但GObject的设计初衷是为了解决多语言交互问题,所以必须设计出一可以容纳任何数据类型的通用数据类型。以便在不同的编程语言之间进行数据转换操作。这个数据类型就是GValue。因此GValue就是一个变量容器,可以存放各种变量的值,例如整形数、指针、GObject子类等等,本章节GValue存放指针变量的值。更多GValue的理解,我会另写一篇文章记录。