glib--接口的实现

interface类似于C++中的抽象类。

分三部分:接口类,实现接口的类,测试函数。

这次的文件分了好几个:

    my-iusb.h

    #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);
     
    // 这是一个usb接口
    // 这个接口既可以read又可以write,read只能返回字符串,write只能接受字符串
    #endif


my-iusb.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)
    {
        g_printf("%s %i.\n",__func__, __LINE__);
        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);
        g_printf("%s %i.\n",__func__, __LINE__);
    }
     
    static void my_udisk_init(MyUdisk *self)
    {
        self->data = g_string_new(NULL);
        g_printf("%s %i.\n",__func__, __LINE__);
    }
     
    static void my_udisk_class_init(MyUdiskClass *self)
    {
        g_printf("%s %i.\n",__func__, __LINE__);
    }
     
    static void my_iusb_interface_init(MyIUsbInterface *iface)
    {
        iface->read = my_udisk_read;
        iface->write = my_udisk_write;
        g_printf("%s %i.\n",__func__, __LINE__);
    }


上面的文件构成了接口,下面的文件是具体的使用者

     my-udisk.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


    u-disk.c

    #include "my-iusb.h"
     
    G_DEFINE_INTERFACE (MyIUsb, my_iusb, G_TYPE_INVALID);
     
    static void my_iusb_default_init(MyIUsbInterface *iface)
    { // 这是函数是宏G_DEFINE_INTERFACE展开后的一个函数
    }
     
    gchar *my_iusb_read(MyIUsb *self)
    {
        g_printf("%s %i.\n",__func__, __LINE__);
        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_printf("%s %i.\n",__func__, __LINE__);
        g_return_if_fail(MY_IS_IUSB(self));
        MY_IUSB_GET_INTERFACE(self)->write(self, str);
    }

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

    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 所规定的接口。


   接下来是main.c

    #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;
    }


最后放上MAKEFILE

    all:
        gcc -g `pkg-config --cflags --libs gobject-2.0` my-iusb.c my-udisk.c main.c -o test


输入结果如下:

    my_udisk_class_init 30.
    my_iusb_interface_init 37.
    my_udisk_init 25.
    my_iusb_write 18.
    my_udisk_write 19.
    my_iusb_read 11.
    my_udisk_read 10.
    I am u-disk!
     
    Is udisk a MyIUsb object?
    Yes!
     
    Is udisk a MyUdisk object?
    Yes!


下面我要粘贴的是原作者写的: 经常要用到并且需要自己定义的宏

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

    #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 的子类化,在声明类的时候,在头文件中直接插入类似下面的一组宏定义:

    #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 接口的类结构体类型。
---------------------  
作者:DawnRayYang  
来源:CSDN  
原文:https://blog.csdn.net/xbl1986/article/details/6722783?utm_source=copy  
版权声明:本文为博主原创文章,转载请附上博文链接!

另一个例子:

本部分介绍如何定义一个接口,个人感觉介绍GObject中如何使用接口的资料还是比较少的。。。。XD
一般的类的继承可以分为两种,一种就是传统的,另一种则是接口形式的。
在使用方面有些差异,前者调用一个类的成员,而后者则将某个类转换成抽象的接口,只使用接口中定义的方法。
在GObject中,按照个人的理解。所谓接口还是一个类结构,只不过这个类结构不被实例化,但其class结构还是要定义的。
假设我们定义一个名为Ihuman的接口,里面定义名为speak的方法。
 
#define JC_TYPE_IHUMAN  (jc_ihuman_get_type ())
 
#define JC_IHUMAN(obj)  (G_TYPE_CHECK_INSTANCE_CAST ((obj), JC_TYPE_IHUMAN, JcIhuman))
 
#define JC_IS_IHUMAN(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj), JC_TYPE_IHUMAN))
 
#define JC_IHUMAN_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), JC_TYPE_IHUMAN, JcIhumanInterface))
 
宏定义少了几个,因为不需要定义实例结构,这里面INTERFACE结构可以与前文中CLASS结构相对应.
 
 
typedef struct _JcIhuman   JcIhuman; /* dummy object */
 
typedef struct _JcIhumanInterface      JcIhumanInterface;
 
注意我们typedef了JcIhuman,但实际上并没有真正的定义,我们仅仅需要定义JcIhumanInterface,如下,
 
struct _JcIhumanInterface
{
 GTypeInterface parent_iface;
 void (*speak) ();
};
Interface中我们自己要定义的只有一个指向函式的指针
 
当然还是少不了一个类型获取函式和一个对外的函式.
GType jc_ihuman_get_type (void);
void jc_people_talk (JcIhuman *self);
下面看如何实现,
GType
jc_ihuman_get_type (void)
{
 static GType iface_type = 0;
 if (iface_type == 0)
    {
      static const GTypeInfo info = {
        sizeof (JcIhumanInterface),
        NULL,   /* base_init */
        NULL,   /* base_finalize */
      };
      iface_type = g_type_register_static (G_TYPE_INTERFACE, "JcIhuman",&info, 0);
    }
 return iface_type;
}
在GTypeInfo中有两个NULL,分别对应的是base_init和base_finalize
前文中有简单的介绍一个类初始化和终结的过程。
void
 
jc_people_talk (JcIhuman *self)
{
 g_return_if_fail (JC_IS_IHUMAN (self));
 JC_IHUMAN_GET_INTERFACE (self)->speak ();
}
如果在调用时,我们传入的肯定是一个实现了该接口的类的实例。所以在调用的时候要使用宏定义JC_HUMAN_GET_INTERFACE.
 
我们看到上面的结构中并没有定义类似class_init的结构,但并不意味就不定义了,在接口的实现时还是要定义一个名为interface_init结构。
假设我们定义的Boy类要实现speak这一Ihuman定义的接口,要增加如下的代码
G_DEFINE_TYPE_WITH_CODE (JcBoy, jc_boy, JC_TYPE_BABY,//);
                        G_IMPLEMENT_INTERFACE (JC_TYPE_IHUMAN,
                        jc_ihuman_interface_init));
这里就不用G_DEFINE_TYPE了,而是使用G_DEFINE_TYPE_WITH_CODE,而这两个宏本身都是对其它宏的简化,因为前文简单介绍过,这里就不展开了。
其中G_IMPLEMENT_INTERFACE是声明这个类要实现接口,并且指定了接口初始化的函式指针,其展开后如下,
 
#define G_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init)       { \
 const GInterfaceInfo g_implement_interface_info = { \
    (GInterfaceInitFunc) iface_init, NULL, NULL \
 }; \
 g_type_add_interface_static (g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
}
 
下面看接口实现的函式
static void speak()
{
    g_print("la la la ... haha ! I'm a boy!\n");
}
static void
jc_ihuman_interface_init(JcIhumanInterface *iface)
{
 iface->speak =speak;
}
interface_init其中就是简单做了一个指针的赋值,仅此而已。
至此一个接口的定义与实现就完成了。
 
而调用起来也是比较容易的,
JcBoy *boy = g_object_new (JC_TYPE_BOY, NULL);
jc_people_talk(boy);

猜你喜欢

转载自blog.csdn.net/evsqiezi/article/details/83049655