分类: LINUX
2011-08-29 17:24:06
在文档 [] 中谈到接口古已有之,但是类的继承赋予了它一些新的概念。本文结合实例,学习如何使用 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 #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 |
上述代码与文档[]中 KbBibtex 类的声明代码很相似,但也有所区别。
首先,MyIUsb 接口的实例结构体,它只是个名字,并没有具体实现。这是因为,在 Java 那样的语言里谈到“接口”,那么则意味着它是无法实例化的。这其中是有一定的道理的,因为接口只是协议嘛。
其次,MyIUsbInterface 是 MyIUsb 接口的类结构体,它继承自 GTypeInterface 类结构体。也就是说,当你要声明接口时,那么接口的类结构体便要继承 GTypeInterface 类结构体,而当你声明的是可实例化为对象的类时,其类结构体便要继承 GObjectClass。
再次,在 MyIUsbInterface 结构体中,可包含一组函数指针,它们便是接口的协议。
最后,声明接口。对于本例而言,接口便是 my_iusb_read 与 my_iusb_write。
上述代码的解读过程大致是:
建立 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 宏的展开代码中声明的一个函数,其中可以放置接口的一些初始化代码。如果没有这方面的需求,就让它表现为一个空函数即可,否则编译器会警告你,说你有一个 函数声明了但没有实现。
另外,上述代码中出现了三个陌生的宏,其功能如下:
1 | #define MY_IS_IUSB(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_IUSB)) |
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 #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 |
文档 [-] 中自定义了多个宏,在此略微进行总结一下,免的后续文档再多费口舌。
对于 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)) |
这些宏的用法总结如下:
对于 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)) |
既然已经有了 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; } |
上述代码中,有几处需要留意的地方:
至此,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]
[3]
[4]
[5]