في Gobject، وكيفية تجاوز أسلوب فئة الأم تنتمي إلى واجهة؟

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

  •  05-07-2019
  •  | 
  •  

سؤال

وGObject فئة A تنفذ واجهة IA، B هو فئة مشتقة من A. B كيف يمكن تجاوز الأسلوب والتي هي جزء من واجهة IA؟

وأو، هل هذا ممكن في GObject؟

وأنا أعرف كيفية تجاوز الأساليب الفئة الأصل، ولكن عندما يلتقي الميراث واجهة، والأشياء ويبدو أن أكثر تعقيدا.

وشكرا جزيلا!

هل كانت مفيدة؟

المحلول

نعم، فمن الممكن: مجرد واجهة reimplement كما كان أول مرة، إما باستخدام G_IMPLEMENT_INTERFACE() أو دليل تهيئة أنه في وظيفة get_type() بك

والألم الحقيقي هو إذا كنت بحاجة إلى سلسلة حتى الطريقة القديمة. في هذه الحالة، يجب أن تلعب مع g_type_interface_peek_parent للحصول على الدرجة الواجهة السابقة.

وهنا هو حالة اختبار:

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

نصائح أخرى

وأعتقد أن أفضل حل هو جعل الأسلوب في الظاهرية، بدلا من أن يكون B إعادة تنفيذ-يتم إرفاق واجهة A إلى (وهذا قد يتطلب المزيد من العمل من مجرد إعادة تحديد وظيفة واحدة)، والتي يمكنك القيام به مثل هذا (على سبيل المثال يجب أن تكون كاملة ما عدا تعريف واجهة fooable):

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

وهذا هو موجز اعتبارا من مثال ما أستطيع تحقيق ذلك. عند استدعاء fooable_foo() فإن وظيفة تبدو في vtable من أجل وظيفة محددة عند تنفيذ واجهة وهو a_foo() التي تبدو في vtable فئة لتحديد وظيفة للاتصال الواقع. تعريف الفئة B يتجاوز a_foo_real() فئة مع تلقاء نفسها. إذا كنت بحاجة إلى B الفئة التي b_foo_real إلى سلسلة يصل، وهذا هو وسيلة سهلة بما فيه الكفاية (استخدام A_CLASS(b_parent_class)->foo() الذي يعرف لك في الماكرو G_DEFINE_TYPE)

scroll top