Gobject で、インターフェイスに属する親クラスのメソッドをオーバーライドするにはどうすればよいですか?

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

  •  05-07-2019
  •  | 
  •  

質問

GObject クラス A はインターフェイス IA を実装し、B は A の派生クラスです。B は、インターフェイス IA の一部である A のメソッドをオーバーライドするにはどうすればよいでしょうか?

それとも、GObject でこれは可能でしょうか?

親クラスのメソッドをオーバーライドする方法は知っていますが、継承がインターフェイスと出会うと、事態はさらに複雑になるようです。

どうもありがとう!

役に立ちましたか?

解決

はい、可能です:最初と同じようにインターフェイスを再実装するだけです。 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を再実装するのではなく、Aのメソッドを仮想化することだと思います(これは、1つの関数を再定義するよりも多くの作業が必要な場合があります)。 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クラスの a_foo_real()をそれ自身でオーバーライドします。チェーンするためにBクラスの b_foo_real が必要な場合、それは十分簡単です(G_DEFINE_TYPEマクロで定義されている A_CLASS(b_parent_class)-&gt; foo()を使用します)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top