GObject学习教程---第八章:GObject 对接口的模拟

官网API:https://developer.gnome.org/gobject/stable/

本文是学习学习他人的博客的心得(具体详见“楼主见解”),如果源网站可访问的话,建议直接访问源网站:

楼主见解:

主要讲解接口是如何实现的。

整理一下常用宏:

继承常用宏:其中P表示项目名称,T表示类名称,PTPrivate表示私有数据结构体。

#define P_TYPE_T (p_t_get_type ())
#define P_T(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), P_TYPE_T, PT))
#define P_IS_T(obj) G_TYPE_CHECK_INSTANCE_TYPE ((obj), P_TYPE_T))
#define P_T_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), P_TYPE_T, PTClass))
#define P_IS_T_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), P_TYPE_T))
#define P_T_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), P_TYPE_T, PTClass))
#define P_T_GET_PRIVATE(obj) (\
        G_TYPE_INSTANCE_GET_PRIVATE ((obj), P_TYPE_T, PTPrivate))
  • P_TYPE_T:仅在使用 g_object_new 进行对象实例化的时候使用一次,用于向 GObject 库的类型系统注册 PT 类;
  • P_T (obj):用于将 obj 对象的类型强制转换为 P_T 类的实例结构体类型;
  • P_IS_T (obj):用于判断 obj 对象的类型是否为 P_T 类的实例结构体类型;
  • P_T_CLASS(klass):用于将 klass 类结构体的类型强制转换为 P_T 类的类结构体类型;
  • P_IS_T_CLASS(klass):用于判断 klass 类结构体的类型是否为 P_T 类的类结构体类型;
  • P_T_GET_CLASS(obj):获取 obj 对象对应的类结构体类型。
  • P_T_GET_PRIVATE(obj):获取obj对象对应的私有数据。

接口常用宏:其中P表示项目名称,T表示类名称,I是接口的缩写

#define P_TYPE_IT (p_t_get_type ())
#define P_IT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), P_TYPE_IT, PIt))
#define P_IS_IT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), P_TYPE_IT))
#define P_IT_GET_INTERFACE(obj) \
        (G_TYPE_INSTANCE_GET_INTERFACE ((obj), P_TYPE_IT, PItInterface))
  • P_TYPE_IT:仅在接口实现时使用一次,用于向 GObject 库的类型系统注册 PIT 接口;
  • P_IT (obj):用于将 obj 对象的类型强制转换为 P_IT 接口的实例结构体类型;
  • P_IS_IT (obj):用于判断 obj 对象的类型是否为 P_IT 接口的实例结构体类型;
  • P_IT_GET_INTERFACE(obj):获取 obj 对象对应的 P_IT 接口的类结构体类型。

接口的定义:

第一:.h实现

#ifndef MY_IUSB_H
#define MY_IUSB_H
  
#include <glib-object.h>
  
/*
写之前需要确定的三个名称如下(其中I表示接口):
类名称:MyIUse;对应的接口类(结构类)MyIUserInterface(MyIUserClass)
类型:MY_TYPE_IUSE(一般定义取类名的大写,中间加TYPE组合)
函数前缀:my_iuse(一般定义取类名的小写,中间加下划线组合)
*/
#define MY_TYPE_IUSB (my_iusb_get_type ())
#define MY_IUSB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),MY_TYPE_IUSB, MyIUsb))
#define MY_IS_IUSB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_IUSB))
#define MY_IUSB_GET_INTERFACE(obj) (\
                G_TYPE_INSTANCE_GET_INTERFACE ((obj), MY_TYPE_IUSB, MyIUsbInterface))
  
//类结构无需具体是实现
typedef struct _MyIUsb MyIUsb;

typedef struct _MyIUsbInterface MyIUsbInterface;
struct _MyIUsbInterface {
    GTypeInterface parent_interface;

    //接口  
    gchar * (*read) (MyIUsb *self);
    void (*write) (MyIUsb *self, const gchar *str);
};
 
 
GType my_iusb_get_type (void);
//接口定义
gchar * my_iusb_read (MyIUsb *self);
void my_iusb_write (MyIUsb *self, const gchar *str);
  
#endif

第二:.c实现

#include "my-iusb.h"
 
G_DEFINE_INTERFACE (MyIUsb, my_iusb, G_TYPE_INVALID);
 
static void
my_iusb_default_init (MyIUsbInterface *iface)
{
}
 
gchar *
my_iusb_read (MyIUsb *self)
{
        g_return_if_fail (MY_IS_IUSB (self));
 
        MY_IUSB_GET_INTERFACE (self)->read (self);
}
 
void
my_iusb_write (MyIUsb *self, const gchar *str)
{
        g_return_if_fail (MY_IS_IUSB (self));
 
        MY_IUSB_GET_INTERFACE (self)->write (self, str);
}

接口的实现类:

同样继承自GObject,基本和GObject继承一致,唯一的区别就是在.c文件中用G_DEFINE_TYPE_WITH_CODE代替G_DEFINE_TYPE,其中前者比后者参数多一个,这个就是接口实现。多一个接口初始化函数,此函数my_iusb_interface_init,此函数中,将接口的函数指针,指向实际执行的函数。这样就可以通过接口调用之类的接口了。

第一:.h实现

#ifndef MY_UDISK_H
#define MY_UDISK_H
 
#include "my-iusb.h"
 
#define MY_TYPE_UDISK (my_udisk_get_type ())
#define MY_UDISK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_UDISK, MyUdisk))
#define MY_IS_UDISK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_UDISK))
#define MY_UDISK_CLASS(klass) \
        (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_UDISK, MyUdiskClass))
#define MY_IS_UDISK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_UDISK))
#define MY_UDISK_GET_CLASS(obj) \
        (G_TYPE_INSTANCE_GET_CLASS ((obj),MY_TYPE_UDISK,MyUdiskClass))
 
typedef struct _MyUdisk MyUdisk;
typedef struct _MyUdiskClass MyUdiskClass;
 
struct _MyUdisk {
        GObject parent;
        GString *data;
};
struct _MyUdiskClass {
        GObjectClass parent_class;
};
 
GType my_udisk_get_type (void);
 
#endif

第二:.c实现

#include "my-udisk.h"
 
static void my_iusb_interface_init (MyIUsbInterface *iface);
 
G_DEFINE_TYPE_WITH_CODE (MyUdisk, my_udisk, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (MY_TYPE_IUSB, my_iusb_interface_init));
 
static gchar *
my_udisk_read (MyIUsb *iusb)
{
        MyUdisk *udisk = MY_UDISK (iusb);
        return udisk->data->str;
}
 
static void
my_udisk_write (MyIUsb *iusb, const gchar *str)
{
        MyUdisk *udisk = MY_UDISK (iusb);
        g_string_assign (udisk->data, str);
}
 
static void
my_udisk_init (MyUdisk *self)
{
        self->data = g_string_new (NULL);
}
 
static void
my_udisk_class_init (MyUdiskClass *self)
{
}
 
static void
my_iusb_interface_init (MyIUsbInterface *iface)
{
        iface->read = my_udisk_read;
        iface->write = my_udisk_write;
}

第三:测试代码

#include "my-udisk.h"
 
int
main (void)
{
        g_type_init ();
 
        MyUdisk *udisk = g_object_new (MY_TYPE_UDISK, NULL);
 
        my_iusb_write (MY_IUSB (udisk), "I am u-disk!");
        gchar *data = my_iusb_read (MY_IUSB (udisk));
 
        g_printf ("%s\n\n", data);
 
        g_printf ("Is udisk a MyIUsb object?\n");
        if (MY_IS_IUSB (udisk))
                g_printf ("Yes!\n");
        else
                g_printf ("No!\n");
 
        g_printf ("\nIs udisk a MyUdisk object?\n");
        if (MY_IS_UDISK (udisk))
                g_printf ("Yes!\n");
        else
                g_printf ("No!\n");
 
        return 0;
}

源博客网址:http://garfileo.is-programmer.com/categories/6934/posts

GObject 对接口的模拟

在文档 [1] 中谈到接口古已有之,但是类的继承赋予了它一些新的概念。本文结合实例,学习如何使用 GObject 库所提供的接口类型来表达这些概念。

接口声明

下面的代码(文件名 my-iusb.h)声明了一个叫做 MyIUsb 的接口,My 是项目名,I 是 interface 的首字母的大写,Usb 表示接口的名称。MyIUsb 就表示在“My”项目里,Usb 是一个 Interface。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

#ifndef MY_IUSB_H

#define MY_IUSB_H

  

#include <glib-object.h>

  

#define MY_TYPE_IUSB (my_iusb_get_type ())

#define MY_IUSB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),MY_TYPE_IUSB, MyIUsb))

typedef struct _MyIUsb MyIUsb;

typedef struct _MyIUsbInterface MyIUsbInterface;

  

struct _MyIUsbInterface {

    GTypeInterface parent_interface;

  

    gchar * (*read) (MyIUsb *self);

    void (*write) (MyIUsb *self, const gchar *str);

};

GType my_iusb_get_type (void);

gchar * my_iusb_read (MyIUsb *self);

void my_iusb_write (MyIUsb *self, const gchar *str);

  

#endif

上述代码与文档[5]中 KbBibtex 类的声明代码很相似,但也有所区别。

首先,MyIUsb 接口的实例结构体,它只是个名字,并没有具体实现。这是因为,在 Java 那样的语言里谈到“接口”,那么则意味着它是无法实例化的。这其中是有一定的道理的,因为接口只是协议嘛。

其次,MyIUsbInterface 是 MyIUsb 接口的类结构体,它继承自 GTypeInterface 类结构体。也就是说,当你要声明接口时,那么接口的类结构体便要继承 GTypeInterface 类结构体,而当你声明的是可实例化为对象的类时,其类结构体便要继承 GObjectClass。

再次,在 MyIUsbInterface 结构体中,可包含一组函数指针,它们便是接口的协议。

最后,声明接口。对于本例而言,接口便是 my_iusb_read 与 my_iusb_write。

上述代码的解读过程大致是:

  1. 这是一个 usb 接口。
  2. 这个接口即可以 read,也可以 write,但是 read 只能是返回字符串,而 write 只能是接受字符串
  3. 声明这个接口的具体形式,在现实中则以理解为声明 usb 接口有几根线构成,每根线的功能等等。

接口的定义

建立 my-iusb.c 源文件,内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

#include "my-iusb.h"

G_DEFINE_INTERFACE (MyIUsb, my_iusb, G_TYPE_INVALID);

static void

my_iusb_default_init (MyIUsbInterface *iface)

{

}

gchar *

my_iusb_read (MyIUsb *self)

{

        g_return_if_fail (MY_IS_IUSB (self));

        MY_IUSB_GET_INTERFACE (self)->read (self);

}

void

my_iusb_write (MyIUsb *self, const gchar *str)

{

        g_return_if_fail (MY_IS_IUSB (self));

        MY_IUSB_GET_INTERFACE (self)->write (self, str);

}

其中,my_iusb_default_init 是 G_DEFINE_INTERFACE 宏的展开代码中声明的一个函数,其中可以放置接口的一些初始化代码。如果没有这方面的需求,就让它表现为一个空函数即可,否则编译器会警告你,说你有一个函数声明了但没有实现。

另外,上述代码中出现了三个陌生的宏,其功能如下:

  • G_DEFINE_INTERFACE 宏的功用与 G_DEFINE_TYPE 类似,后者在 GObject 子类化的时候经常使用;
  • MY_IS_IUSB 宏是用来检测对象是否为 MyIUsb 类型,最好要在 my-iusb.h 中进行定义,代码为:

1

#define MY_IS_IUSB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_IUSB))

  • 在 GObject 子类化的时候,也可以定义类似的宏,用于识别某个对象对应哪种类型。比如之前我们用过的一个 G_IS_OBJECT 宏,它可以识别对象是否为 GObject 类型的对象。
  • MY_IUSB_GET_INTERFACE 宏,用于从 MyIUsb 接口的实例结构体中取出类结构体指针,然后利用该指针访问接口对应的方法。至于 MyIUsb 接口的实例结构体是怎样与类结构体指针取得关联的,那是 GObject 的内幕,暂且不必关心。MY_IUSB_GET_INTERFACE 宏也需要在 my-iusb.h 中进行定义,如下:

1

2

#define MY_IUSB_GET_INTERFACE(obj) (\

                G_TYPE_INSTANCE_GET_INTERFACE ((obj), MY_TYPE_IUSB, MyIUsbInterface))

现在,将 my-iusb.h 文件更新为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#ifndef MY_IUSB_H

#define MY_IUSB_H

  

#include <glib-object.h>

  

#define MY_TYPE_IUSB (my_iusb_get_type ())

#define MY_IUSB(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),MY_TYPE_IUSB, MyIUsb))

#define MY_IS_IUSB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_IUSB))

#define MY_IUSB_GET_INTERFACE(obj) (\

                G_TYPE_INSTANCE_GET_INTERFACE ((obj), MY_TYPE_IUSB, MyIUsbInterface))

  

typedef struct _MyIUsb MyIUsb;

typedef struct _MyIUsbInterface MyIUsbInterface;

  

struct _MyIUsbInterface {

    GTypeInterface parent_interface;

  

    gchar * (*read) (MyIUsb *self);

    void (*write) (MyIUsb *self, const gchar *str);

};

GType my_iusb_get_type (void);

gchar * my_iusb_read (MyIUsb *self);

void my_iusb_write (MyIUsb *self, const gchar *str);

  

#endif

插曲:经常要用到并且需要自己定义的宏

文档 [2-5] 中自定义了多个宏,在此略微进行总结一下,免的后续文档再多费口舌。

对于 GObject 的子类化,那么在声明类的时候,在头文件中直接插入类似下面的一组宏定义:

1

2

3

4

5

6

#define P_TYPE_T (p_t_get_type ())

#define P_T(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), P_TYPE_T, PT))

#define P_IS_T(obj) G_TYPE_CHECK_INSTANCE_TYPE ((obj), P_TYPE_T))

#define P_T_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), P_TYPE_T, PTClass))

#define P_IS_T_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), P_TYPE_T))

#define P_T_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), P_TYPE_T, PTClass))

这些宏的用法总结如下:

  • P_TYPE_T:仅在使用 g_object_new 进行对象实例化的时候使用一次,用于向 GObject 库的类型系统注册 PT 类;
  • P_T (obj):用于将 obj 对象的类型强制转换为 P_T 类的实例结构体类型;
  • P_IS_T (obj):用于判断 obj 对象的类型是否为 P_T 类的实例结构体类型;
  • P_T_CLASS(klass):用于将 klass 类结构体的类型强制转换为 P_T 类的类结构体类型;
  • P_IS_T_CLASS(klass):用于判断 klass 类结构体的类型是否为 P_T 类的类结构体类型;
  • P_T_GET_CLASS(obj):获取 obj 对象对应的类结构体类型。

对于 GTypeInterface 的子类化,在声明类的时候,在头文件中直接插入类似下面的一组宏定义:

1

2

3

4

5

#define P_TYPE_IT (p_t_get_type ())

#define P_IT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), P_TYPE_IT, PIt))

#define P_IS_IT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), P_TYPE_IT))

#define P_IT_GET_INTERFACE(obj) \

        (G_TYPE_INSTANCE_GET_INTERFACE ((obj), P_TYPE_IT, PItInterface))

  • P_TYPE_IT:仅在接口实现时使用一次,用于向 GObject 库的类型系统注册 PIT 接口;
  • P_IT (obj):用于将 obj 对象的类型强制转换为 P_IT 接口的实例结构体类型;
  • P_IS_IT (obj):用于判断 obj 对象的类型是否为 P_IT 接口的实例结构体类型;
  • P_IT_GET_INTERFACE(obj):获取 obj 对象对应的 P_IT 接口的类结构体类型。

接口的实现——制造 U 盘

既然已经有了 USB 接口的声明(协议),那就意味着我们可以制造具备这种接口的类与对象了。

首先声明 U 盘类(my-udisk.h):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#ifndef MY_UDISK_H

#define MY_UDISK_H

#include "my-iusb.h"

#define MY_TYPE_UDISK (my_udisk_get_type ())

#define MY_UDISK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_UDISK, MyUdisk))

#define MY_IS_UDISK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_UDISK))

#define MY_UDISK_CLASS(klass) \

        (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_UDISK, MyUdiskClass))

#define MY_IS_UDISK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_UDISK))

#define MY_UDISK_GET_CLASS(obj) \

        (G_TYPE_INSTANCE_GET_CLASS ((obj),MY_TYPE_UDISK,MyUdiskClass))

typedef struct _MyUdisk MyUdisk;

typedef struct _MyUdiskClass MyUdiskClass;

struct _MyUdisk {

        GObject parent;

        GString *data;

};

struct _MyUdiskClass {

        GObjectClass parent_class;

};

GType my_udisk_get_type (void);

#endif

上述代码声明了一个 MyUdisk 类,它是 GObject 的子类。MyUdisk 类的实例结构体中有一个 GString 类型的 data 属性,用于存储数据。也就是说,这个 U 盘设计的有些脑残,因为它只能存储一个字符串!

然后定义 U 盘类(my-udisk.c):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

#include "my-udisk.h"

static void my_iusb_interface_init (MyIUsbInterface *iface);

G_DEFINE_TYPE_WITH_CODE (MyUdisk, my_udisk, G_TYPE_OBJECT,

                         G_IMPLEMENT_INTERFACE (MY_TYPE_IUSB, my_iusb_interface_init));

static gchar *

my_udisk_read (MyIUsb *iusb)

{

        MyUdisk *udisk = MY_UDISK (iusb);

        return udisk->data->str;

}

static void

my_udisk_write (MyIUsb *iusb, const gchar *str)

{

        MyUdisk *udisk = MY_UDISK (iusb);

        g_string_assign (udisk->data, str);

}

static void

my_udisk_init (MyUdisk *self)

{

        self->data = g_string_new (NULL);

}

static void

my_udisk_class_init (MyUdiskClass *self)

{

}

static void

my_iusb_interface_init (MyIUsbInterface *iface)

{

        iface->read = my_udisk_read;

        iface->write = my_udisk_write;

}

上述代码中,有几处需要留意的地方:

  • my_iusb_interface_init 函数声明必须要放在 G_DEFINE_TYPE_WITH_CODE 宏之前,因为这个宏的展开代码中需要使用这个函数;
  • G_DEFINE_TYPE_WITH_CODE 是文档 [2-5] 中出现过的 G_DEFINE_TYPE 宏的“扩展版本”,在本例中可以向 my_udisk_get_type 函数(即 MY_TYPE_UDISK 宏展开的那个函数)中插入 C 代码。在本例中,这个宏所插入的 C 代码是“G_IMPLEMENT_INTERFACE(MY_TYPE_IUSB,my_iusb_interface_init)”,其中 G_IMPLEMENT_INTERFACE 宏的作用是将接口添加到  MyUdisk 类中;
  • my_iusb_interface_init 函数的作用是表明 MyUdisk 类实现了 MyIUsb 所规定的接口。

至此,MyIUsb 接口的一个实现便完成了。下面的 main.c 文件中的代码用于测试这个 U 盘是否可以使用 MyIUsb 接口进行访问,即:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#include "my-udisk.h"

int

main (void)

{

        g_type_init ();

        MyUdisk *udisk = g_object_new (MY_TYPE_UDISK, NULL);

        my_iusb_write (MY_IUSB (udisk), "I am u-disk!");

        gchar *data = my_iusb_read (MY_IUSB (udisk));

        g_printf ("%s\n\n", data);

        g_printf ("Is udisk a MyIUsb object?\n");

        if (MY_IS_IUSB (udisk))

                g_printf ("Yes!\n");

        else

                g_printf ("No!\n");

        g_printf ("\nIs udisk a MyUdisk object?\n");

        if (MY_IS_UDISK (udisk))

                g_printf ("Yes!\n");

        else

                g_printf ("No!\n");

        return 0;

}

这个程序的编译命令及执行结果如下:

1

2

3

4

5

6

7

8

9

$ gcc $(pkg-config --cflags --libs gobject-2.0) my-iusb.c my-udisk.c main.c -o test

$ ./test

I am u-disk!

Is udisk a MyIUsb object?

Yes!

Is udisk a MyUdisk object?

Yes!

一切都在掌握之中啊。

还可以接着造移动硬盘

可以像制造 U 盘那样,便可以炮制移动硬盘了。这项艰巨的任务便交给你了,而懒惰的我只提供下面这个计算机主机程序:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

#include "my-udisk.h"

int

main (void)

{

        g_type_init ();

        gchar *data = NULL;

        MyUdisk *udisk = g_object_new (MY_TYPE_UDISK, NULL);

        my_iusb_write (MY_IUSB (udisk), "I am an u-disk!");

        data = my_iusb_read (MY_IUSB (udisk));

        g_printf ("%s\n", data);

        MyPortableHardDisk *phdisk = g_object_new (MY_TYPE_PORTABLE_HARD_DISK, NULL);

        my_iusb_write (MY_IUSB (phdisk), "I am a portable hard disk!");

        data = my_iusb_read (MY_IUSB (phdisk));

        g_printf ("%s\n", data);

        return 0;

}

~ End ~

参考文档

[1继承与接口

[2使用 GObject 库模拟类的数据封装形式

[3GObject 子类私有属性模拟

[4GObject 子类私有属性的外部访问

[5温故而知新

转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com

猜你喜欢

转载自blog.csdn.net/knowledgebao/article/details/82458349
今日推荐