In gobject, wie Eltern-Klasse-Methode gehört zu einer Schnittstelle außer Kraft zu setzen?

StackOverflow https://stackoverflow.com/questions/1606669

  •  05-07-2019
  •  | 
  •  

Frage

GObject Klasse A implementiert Schnittstelle IA, B eine abgeleitete Klasse von A. Wie kann Überschreibung A Methode B, die Teil der Schnittstelle IA ist?

Oder ist dies möglich, in GObject?

Ich weiß, wie übergeordnete Klasse Methoden außer Kraft zu setzen, aber als Erbe-Schnittstelle erfüllt, scheinen die Dinge komplizierter zu sein.

Vielen Dank!

War es hilfreich?

Lösung

Ja, es ist möglich. Nur die Schnittstelle neu implementieren, da es das erste Mal war, entweder mit G_IMPLEMENT_INTERFACE() oder manuell in Ihrer get_type() Funktion initialisiert

Der eigentliche Schmerz ist, wenn Sie die alte Methode zu verketten müssen. In diesem Fall sollten Sie spielen mit g_type_interface_peek_parent die bisherige Interface-Klasse zu erhalten.

Hier ist ein Testfall:

/* 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;
}

Andere Tipps

Ich denke, eine bessere Lösung A ist zu machen wäre Methode virtuell, anstatt hat B neu implementieren die Schnittstelle A angebracht ist (dies kann mehr Arbeit erfordern, als nur eine Funktion neu zu definieren), die Sie wie dies tun können (Beispiel vollständig auf andere als die fooable Schnittstellendefinition sollte):

#include <glib-object.h>
#include "fooable.h"

typedef struct {GObject parent;} A;
typedef struct {
    GObjectClass parent;
    gint (*foo) (Fooable *self, gdouble quux);
} AClass;

#define TYPE_A           (a_get_type())
#define A_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST((cls), TYPE_A, AClass))
#define A_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_A, AClass))

gint a_foo_real (Fooable *self, gdouble quux) {
    g_print("a_foo_real(%g)\n", quux);
    return 5;
}

gint a_foo (Fooable *self, gdouble quux) {
    return A_GET_CLASS(self)->foo(self, quux);
}

void implement_fooable (FooableIface *iface) {iface->foo = a_foo;}
void a_class_init      (AClass *cls)         {cls->foo = a_foo_real;}
void a_init            (A *self)             {}

G_DEFINE_TYPE_WITH_CODE(A, a, G_TYPE_OBJECT,
    G_IMPLEMENT_INTERFACE(TYPE_FOOABLE, implement_fooable));

/* derive class B from A  */
typedef struct {A parent;} B;
typedef struct {AClass parent;} BClass;

#define TYPE_B (b_get_type())

gint b_foo_real (Fooable *self, gdouble quux) {
    g_print("b_foo_real(%g)\n", quux);
    return 55;
}

void b_class_init (BClass *cls) {A_CLASS(cls)->foo = b_foo_real;}
void b_init       (B *self)     {}

G_DEFINE_TYPE(B, b, TYPE_A);

int main () {
    g_type_init();
    A *a = g_object_new(TYPE_A, NULL);
    B *b = g_object_new(TYPE_B, NULL);
    fooable_foo(FOOABLE(a), 87.0); // a_foo_real(87.0) and returns 5
    fooable_foo(FOOABLE(b), 32.0); // b_foo_real(32.0) and returns 55
    return 0;
}

Das ist so kurz ein Beispiel, wie ich es machen kann. Wenn Sie anrufen fooable_foo() die Funktion wird auf seiner VTable sucht die Funktion definiert ist, wenn Sie die Schnittstelle implementiert, die a_foo() ist, die bei einer Klasse V-Tabelle schaut, um zu bestimmen, welche Funktion tatsächlich nennen. Die B-Klasse Definition Vorrang vor einer Klasse a_foo_real() mit seinem eigenen. Wenn Sie B Klasse b_foo_real zu verketten müssen, das ist eine einfache genug (Verwendung A_CLASS(b_parent_class)->foo(), die für Sie in der G_DEFINE_TYPE Makro definiert ist)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top