Search code examples
glibgobject

In Gobject, how to override parent class's method belong to an interface?


GObject class A implements interface IA, B is a derived class of A. How can B override A's method that is part of the interface IA?

Or, is this possible in GObject?

I know how to override parent class methods, but when inheritance meets interface, things seems to be more complicated.

Thanks a lot!


Solution

  • Yes, it is possible: just reimplement the interface as it was the first time, either using G_IMPLEMENT_INTERFACE() or manual initializing it in your get_type() function.

    The real pain is if you need to chain up the old method. In this case, you should play with g_type_interface_peek_parent to get the previous interface class.

    Here is a test case:

    /* gcc -otest `pkg-config --cflags --libs gobject-2.0` test.c */
    #include <glib-object.h>
    
    
    /* Interface */
    
    #define TYPE_IFACE   (iface_get_type())
    
    typedef void Iface;
    typedef struct {
        GTypeInterface parent_class;
        void (*action) (Iface *instance);
    } IfaceClass;
    
    GType
    iface_get_type(void)
    {
        static GType type = 0;
    
        if (G_UNLIKELY(type == 0)) {
            const GTypeInfo info = {
                sizeof(IfaceClass), 0,
            };
    
            type = g_type_register_static(G_TYPE_INTERFACE, "Iface", &info, 0);
        }
    
        return type;
    }
    
    void
    iface_action(Iface *instance)
    {
        G_TYPE_INSTANCE_GET_INTERFACE(instance, TYPE_IFACE, IfaceClass)->
            action(instance);
    }
    
    
    /* Base object */
    
    #define TYPE_BASE    (base_get_type())
    
    typedef GObject        Base;
    typedef GObjectClass   BaseClass;
    
    static void
    base_action(Iface *instance)
    {
        g_print("Running base action on a `%s' instance...\n",
                g_type_name(G_TYPE_FROM_INSTANCE(instance)));
    }
    
    static void
    base_iface_init(IfaceClass *iface)
    {
        iface->action = base_action;
    }
    
    G_DEFINE_TYPE_WITH_CODE(Base, base, G_TYPE_OBJECT,
                            G_IMPLEMENT_INTERFACE(TYPE_IFACE, base_iface_init));
    
    static void
    base_class_init(BaseClass *klass)
    {
    }
    
    static void
    base_init(Base *instance)
    {
    }
    
    
    /* Derived object */
    
    #define TYPE_DERIVED (derived_get_type())
    
    typedef Base      Derived;
    typedef BaseClass DerivedClass;
    
    static void
    derived_action(Iface *instance)
    {
        IfaceClass *iface_class, *old_iface_class;
    
        iface_class = G_TYPE_INSTANCE_GET_INTERFACE(instance, TYPE_IFACE, IfaceClass);
        old_iface_class = g_type_interface_peek_parent(iface_class);
    
        g_print("Running derived action on a `%s' instance...\n",
                g_type_name(G_TYPE_FROM_INSTANCE(instance)));
    
        /* Chain up the old method */
        old_iface_class->action(instance);
    }
    
    static void
    derived_iface_init(IfaceClass *iface)
    {
        iface->action = derived_action;
    }
    
    G_DEFINE_TYPE_WITH_CODE(Derived, derived, TYPE_BASE,
                            G_IMPLEMENT_INTERFACE(TYPE_IFACE, derived_iface_init));
    
    static void
    derived_class_init(DerivedClass *klass)
    {
    }
    
    static void
    derived_init(Derived *instance)
    {
    }
    
    
    int
    main()
    {
        GObject *object;
    
        g_type_init();
    
        object = g_object_new(TYPE_BASE, NULL);
        iface_action((Iface *) object);
        g_object_unref(object);
    
        object = g_object_new(TYPE_DERIVED, NULL);
        iface_action((Iface *) object);
        g_object_unref(object);
    
        return 0;
    }